假设我有一个这样定义的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)进行编译, while(1) 会产生错误?
最佳答案
是的。您已经拥有了。
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
您不能将构造函数设为
explicit
,因为这也会导致foo({0})
也会失败。但是您可以简单地使用另一个包装器添加另一层间接:struct AnotherWrapper {
AnotherWrapper(int i): i{i} { }
int i;
};
struct Wrapper {
Wrapper(AnotherWrapper ) { }
};
在这里,
wrap(0)
失败,但是wrap({0})
和wrap(Wrapper{0})
都可以。