C++ 需要没有隐式转换的函数
C++ require function without implicit conversion
我正在使用 boost::variant
来模拟具有值语义的继承。
有一个class可以打印:
struct Printable { /* ... */ };
void print(const Printable &) { /* ... */ }
而class可能不会:
struct NotPrintable { /* ... */ };
最后,有隐式转换的“Base
”class:
struct Base : boost::variant<Printable, NotPrintable>
{
Base(const Printable &) {} // constructor for implicit cast
Base(const NotPrintable &) {} // constructor for implicit cast
};
// Print, if printable, throw exception, if not
void print(const Base &base)
{
Printer printer{};
base.apply_visitor(printer);
}
问题是如何检查访问者内部是否可打印:
struct Printer
{
using result_type = void;
// If printable
template<typename PrintableType> requires
requires(const PrintableType &v) { {print(v)}; } // (1)
void operator()(const PrintableType &v) { print(v); }
// If not printable
void operator()(const auto &v) { throw /*...*/; }
};
由于隐式转换为 const Base &
,要求 (1)
始终为真。如何避免仅在那个确切位置进行转换?
正如@Jarod42在评论中所说,为了确保不发生隐式转换,您需要定义一个模板print()
函数来“吸收”其他类型(NotPrintable
在您的示例)并将其设置为 delete
void print(const auto&) = delete;
那么printable
概念可以定义为
template<class T>
concept printable = requires (const T& x) { print(x); };
要求表达式 print(x)
为 well-formed.
当T
的类型为Printable
或Base
时,表达式print(x)
有效,因为你为它们定义了对应的print()
函数.当T
的类型为NotPrintable
或其他类型时,将调用被删除的print()
,使得表达式ill-formed,因此不满足约束。
那你可以用这个概念来约束Printer::operator()
struct Printer {
using result_type = void;
// If printable
template<printable PrintableType>
void operator()(const PrintableType& v) { print(v); }
// If not printable
void operator()(const auto&) { throw /*...*/; }
};
注意,由于print(const auto&)
可以实例化为any类型,这将禁止all隐式转换,但我们可以通过向其添加约束仍然允许一些隐式转换
#include <concepts>
struct Boolean { };
struct Integer {
Integer();
Integer(Boolean);
friend Integer operator+(Integer, Integer);
};
struct String { };
template<class T, class U>
requires (!std::convertible_to<U, T>)
void operator+(T, U) = delete;
int main() {
Integer{} + Integer{}; // OK
Integer{} + String{}; // ERROR (as expected)
Integer{} + Boolean{}; // OK
};
我找到了一个完美的 solution,它基于 内联好友定义。
根据标准:
Such a function is implicitly an inline function (10.1.6). A friend function defined in a class is in the (lexical)
scope of the class in which it is defined. A friend function defined outside the class is not (6.4.1).
其他一些可能有用的东西:
删除模板函数
void print(auto) = delete;
缺点:
每个函数的样板
禁止所有隐式转换
struct Boolean;
struct String;
struct Integer
{
Integer(Boolean);
friend Integer operator+(Integer, Integer);
}
template<class T, class U>
requires (!std::convertible_to<U, T>)
void operator+(T, U) = delete;
Integer + Integer // OK
Integer + String // ERROR (as expected)
Integer + Boolean // OK
更改界面
struct Base : /* ... */
{
/* ... */
static void print(Base);
}
缺点:
- 不支持运算符 (
Base + Base
-> Base::add(Base, Base)
)
- 界面差了点
- 添加特征
template<typename T> struct Traits {
static constexpr bool is_printable = true;
}
缺点:
- 每个 class 和方法
的样板
- 如何处理隐式转换(
Boolean + Integer
)?
- 延期关闭
我正在使用 boost::variant
来模拟具有值语义的继承。
有一个class可以打印:
struct Printable { /* ... */ };
void print(const Printable &) { /* ... */ }
而class可能不会:
struct NotPrintable { /* ... */ };
最后,有隐式转换的“Base
”class:
struct Base : boost::variant<Printable, NotPrintable>
{
Base(const Printable &) {} // constructor for implicit cast
Base(const NotPrintable &) {} // constructor for implicit cast
};
// Print, if printable, throw exception, if not
void print(const Base &base)
{
Printer printer{};
base.apply_visitor(printer);
}
问题是如何检查访问者内部是否可打印:
struct Printer
{
using result_type = void;
// If printable
template<typename PrintableType> requires
requires(const PrintableType &v) { {print(v)}; } // (1)
void operator()(const PrintableType &v) { print(v); }
// If not printable
void operator()(const auto &v) { throw /*...*/; }
};
由于隐式转换为 const Base &
,要求 (1)
始终为真。如何避免仅在那个确切位置进行转换?
正如@Jarod42在评论中所说,为了确保不发生隐式转换,您需要定义一个模板print()
函数来“吸收”其他类型(NotPrintable
在您的示例)并将其设置为 delete
void print(const auto&) = delete;
那么printable
概念可以定义为
template<class T>
concept printable = requires (const T& x) { print(x); };
要求表达式 print(x)
为 well-formed.
当T
的类型为Printable
或Base
时,表达式print(x)
有效,因为你为它们定义了对应的print()
函数.当T
的类型为NotPrintable
或其他类型时,将调用被删除的print()
,使得表达式ill-formed,因此不满足约束。
那你可以用这个概念来约束Printer::operator()
struct Printer {
using result_type = void;
// If printable
template<printable PrintableType>
void operator()(const PrintableType& v) { print(v); }
// If not printable
void operator()(const auto&) { throw /*...*/; }
};
注意,由于print(const auto&)
可以实例化为any类型,这将禁止all隐式转换,但我们可以通过向其添加约束仍然允许一些隐式转换
#include <concepts>
struct Boolean { };
struct Integer {
Integer();
Integer(Boolean);
friend Integer operator+(Integer, Integer);
};
struct String { };
template<class T, class U>
requires (!std::convertible_to<U, T>)
void operator+(T, U) = delete;
int main() {
Integer{} + Integer{}; // OK
Integer{} + String{}; // ERROR (as expected)
Integer{} + Boolean{}; // OK
};
我找到了一个完美的 solution,它基于 内联好友定义。
根据标准:
Such a function is implicitly an inline function (10.1.6). A friend function defined in a class is in the (lexical) scope of the class in which it is defined. A friend function defined outside the class is not (6.4.1).
其他一些可能有用的东西:
删除模板函数
void print(auto) = delete;
缺点:
每个函数的样板
禁止所有隐式转换struct Boolean; struct String; struct Integer { Integer(Boolean); friend Integer operator+(Integer, Integer); } template<class T, class U> requires (!std::convertible_to<U, T>) void operator+(T, U) = delete; Integer + Integer // OK Integer + String // ERROR (as expected) Integer + Boolean // OK
更改界面
struct Base : /* ... */ { /* ... */ static void print(Base); }
缺点:
- 不支持运算符 (
Base + Base
->Base::add(Base, Base)
) - 界面差了点
- 添加特征
template<typename T> struct Traits { static constexpr bool is_printable = true; }
缺点:
- 每个 class 和方法 的样板
- 如何处理隐式转换(
Boolean + Integer
)? - 延期关闭