27

采取以下最小示例:

using Type1 = std::function<void(void)>;

template <typename T>
using Type2 = std::function<void(T)>;

Type1 whyDoesThisWork;
Type2<void> andYetThisDoesNot;

如果是第二种类型的别名,我会收到错误“参数可能没有 'void' 类型”。(我使用 Xcode 4.5、Clang/c++11/libc++、OS X 10.7 进行了测试。)

我觉得这很奇怪:我本来期望Type1并且Type2<void>行为相同。这里发生了什么?有没有办法重写第二种类型的别名,以便我可以编写Type2<void>和获取std::function<void(void)>而不是错误?

编辑我可能应该补充一点,我想要这样做的原因是允许以下内容:

template <typename ... T>
using Continuation = std::function<void(T...)>;

auto someFunc = []() -> void {
  printf("I'm returning void!\n");
};

Continuation<decltype(someFunc())> c;

Continuation<decltype(someFunc())>变成Continuation<void>了,我得到了错误。

4

5 回答 5

15

我没有实际的答案,只有我在评论中所说的:你不能void作为函数类型,如:

int foo(int, char, void, bool, void, void);     // nonsense!

我相信这T(void)仅允许作为 C 的兼容性表示法(它区分声明原型,与 C++ 非常不同,并且需要能够说“无参数”)。

因此,解决方案应该是可变的:

template <typename ...Args> using myType = std::function<void(Args...)>;

这样你就可以正确地没有参数

myType<> f = []() { std::cout << "Boo\n"; }
于 2012-11-14T02:41:10.557 回答
14

简短的回答是“模板不是字符串替换”。 void f(void)仅当它是void f()C++ 中的别名时才有意义,以便与 C 向后兼容。

第一步是使用可变参数,如其他地方所述。

第二步是弄清楚如何将void返回函数映射到......好吧,可能是类似的东西std::function<void()>,或者可能是其他东西。我说的可能是别的,因为与其他情况不同,你不能打电话std::function<void()> foo; foo( []()->void {} );——这不是真正的延续。

可能是这样的:

template<typename T>
struct Continuation
{
  typedef std::function<void(T)> type;
};

template<>
struct Continuation<void>
{
  typedef std::function<void()> type;
};

然后像这样使用它:

auto someFunc = []()->void {};
Continuation<decltype(someFunc())>::type c;

它为您提供所需的类型。你甚至可以添加一个 apply 到 continuation:

template<typename T>
struct Continuation
{
  typedef std::function<void(T)> type;

  template<typename func, typename... Args>
  static void Apply( type const& cont, func&& f, Args... args)
  {
    cont( f(args...) );
  }
};

template<>
struct Continuation<void>
{
  typedef std::function<void()> type;
  template<typename func, typename... Args>
  static void Apply( type const& cont, func&& f, Args... args)
  {
    f(args...);
    cont();
  }
};

如果传入的类型是 void 或者它是非 void 类型,它可以让您统一地对函数的执行应用延续。

但是,我会问“你为什么要这样做”?

于 2012-11-14T03:56:22.017 回答
8

几个答案已经解释了原因。为了增加这些答案,规范说(C++11 §8.3.5[dcl.func]/4):

由单个非依赖类型的未命名参数组成的参数列表void等效于空参数列表。除此特殊情况外,参数不得具有cv void类型。

在您的Type2示例中,Tinvoid(T)依赖类型——它依赖于模板参数。

于 2012-11-14T04:12:51.493 回答
3

当一个函数被声明为接受一个类型为 的参数时void,就像在 中一样std::function<void(void)>,这实际上只是一种愚蠢的说法,即它接受零个参数。但是您声明 Type2 的方式是std::function带有一个不返回任何内容(void)的签名,但它需要 1 个参数。void 不是可以用作参数的类型,它只是一种声明没有参数的方式。所以它不适用于 Type2,因为这需要一个可以用作参数的实际类型。

于 2012-11-14T02:36:01.613 回答
0

如果将 Void 传递给函数,则可以将其解释为空参数。毕竟你没有使用空指针

void func (void)

变成

void func ()
于 2012-11-14T02:37:05.810 回答