9

假设我有一个FunctionWrapper这样定义的类:

struct FunctionWrapper
{
  FunctionWrapper(std::function<void()> f);

  // ... plus other members irrelevant to the question
};

我想防止从std::function<void()>to进行隐式转换FunctionWrapper,但允许FunctionWrapper使用大括号初始化语法构造 a (即,使用带有单个参数的列表初始化)。换句话说,我想要这样:

void foo();
void wrap(FunctionWrapper);

wrap(foo); // (1) error
wrap({foo}); // (2) OK
wrap(FunctionWrapper{foo}); // (3) OK

有没有办法做到这一点?我在上面定义类的方式不是这样:这允许隐式转换,所以 (1) 编译。

如果我添加explicit到构造函数:

struct FunctionWrapper
{
  explicit FunctionWrapper(std::function<void()> f);

  // ... plus other members irrelevant to the question
};

它也无济于事,因为这“太过分”并且不允许(2)和(1)。

有没有办法实现“中间立场”并让(2)编译而(1)产生错误?

4

1 回答 1

8

有没有办法做到这一点?

是的。你已经有了。

wrap(foo);

为此,它将涉及两个用户定义的转换:void(*)() --> std::function<void()> --> FunctionWrapper,但我们最多只能允许一个用户定义的转换。所以这是一个错误,除非你添加一个单独的构造函数FunctionWrapper来允许它。

wrap({foo}); 

这已经很好了,我们正在复制列表初始化FunctionWrapper,因此上述限制不适用。

wrap(FunctionWrapper{foo});

这显然很好。


请注意,这也为您的第一个示例实际工作的情况提供了前进的道路。假设你有:

struct Wrapper {
    Wrapper(int ) { }
};

foo(0);           // want this to fail
foo({0});         // want this to be OK
foo(Wrapper{0});  // ... and this

您不能制作 constructor explicit,因为这foo({0})也会导致失败。但是您可以简单地使用另一个包装器添加另一层间接:

struct AnotherWrapper {
    AnotherWrapper(int i): i{i} { }
    int i;
};

struct Wrapper {
    Wrapper(AnotherWrapper ) { }
};

在这里,wrap(0)失败了,但wrap({0})wrap(Wrapper{0})可以。

于 2016-11-21T15:33:57.017 回答