防止隐式转换但允许列表初始化?
Prevent implicit conversion but allow list initialisation?
假设我有一个 class 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
有办法实现吗?我在上面定义 class 的方式不是:这允许隐式转换,所以 (1) 编译。
如果我将 explicit
添加到构造函数中:
struct FunctionWrapper
{
explicit FunctionWrapper(std::function<void()> f);
// ... plus other members irrelevant to the question
};
它也无济于事,因为 "too far" 并且不允许 (2) 和 (1)。
有没有办法实现 "middle ground" 并让 (2) 编译而 (1) 产生错误?
Is there a way to achieve that?
是的。你已经拥有了。
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})
都OK。
假设我有一个 class 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
有办法实现吗?我在上面定义 class 的方式不是:这允许隐式转换,所以 (1) 编译。
如果我将 explicit
添加到构造函数中:
struct FunctionWrapper
{
explicit FunctionWrapper(std::function<void()> f);
// ... plus other members irrelevant to the question
};
它也无济于事,因为 "too far" 并且不允许 (2) 和 (1)。
有没有办法实现 "middle ground" 并让 (2) 编译而 (1) 产生错误?
Is there a way to achieve that?
是的。你已经拥有了。
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})
都OK。