防止隐式转换但允许列表初始化?

时间:2016-11-21 14:29:41

标签: c++ implicit-conversion explicit uniform-initialization

假设我有一个类FunctionWrapper定义如下:

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

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

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

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)产生错误?

1 个答案:

答案 0 :(得分:8)

  

有没有办法实现这个目标?

是。你已经拥有它了。

wrap(foo);

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

wrap({foo}); 

这已经很好了,我们是copy-list-initializing 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

您无法创建构造函数explicit,因为这会导致foo({0})失败。但是你可以简单地用另一个包装器添加另一个间接层:

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

struct Wrapper {
    Wrapper(AnotherWrapper ) { }
};

此处wrap(0)失败,但wrap({0})wrap(Wrapper{0})都可以。