不要用于强制执行您的要求。enable_if
使用enable_if
会使函数“消失”,这可能会让用户感到非常困惑。典型症状是错误消息,例如. 这并不能准确地向用户传达违反了要求。error: no matching function for call to expression
static_assert
假设 C++0x,您应该使用 强制执行您的要求。如果您使用的是 C++03,是否应该使用static_assert
(例如 Boost's STATIC_ASSERT
)的仿真是一个折腾,因为这通常意味着将一个错误消息换成另一个错误消息。
对比:
// SFINAE for types that do not decay to int
template<
typename T
, typename = typename std::enable_if<
std::is_same<
typename std::decay<T>::type
, int
>::value
>::type
>
void
f(T&&)
{}
// using static assert instead
template<
typename T
>
void
g(T&&)
{
static_assert( std::is_same<typename std::decay<T>::type, int>::value
, "Constraints violation" );
}
使用 GCC 我得到以下错误f("violation")
(两条消息都带有文件名和行号):
error: no matching function for call to 'f(const char [10])'
另一方面,g("violation")
产生:
error: static assertion failed: "Constraints violation"
现在假设您在断言中使用了清晰、明确的消息,例如foo: parameter type must be CopyConstructible
在模板内部foo
。
话虽如此,SFINAE 和static_assert
有点对立,因此同时拥有明确的约束违规消息和聪明的重载并不总是可能的和/或容易的。
使用Boost.ConceptCheck可以轻松实现您想要做的事情。然而,它确实需要编写离线代码:约束类。我也不认为它static_assert
在可用的地方使用,所以错误消息可能不太好。这在未来可能会改变。
另一种可能性是使用static_assert
+ 类型特征。这种方法的有趣之处在于,对于 C++0x,该库带有一系列有用的特征,您可以直接使用这些特征,而无需编写离线代码。更有趣的是,trait 的使用不仅限于编写约束,它们还可以与 SFINAE 一起使用来进行巧妙的重载。
但是,没有可用的特征来检查类型是否支持特定的操作成员,这可能是由于 C++ 处理函数名称的方式。我们不能使用类似的东西has_member<T, &T::member_to_test_for>
,因为这只有在我们测试的成员首先存在时才有意义(忽略诸如重载之类的事情以及我们还需要将成员的签名传递给特征的事实)。
以下是将任意表达式转换为特征的方法:
template<typename T>
struct void_ {
typedef void type;
};
template<typename T>
struct trait {
private:
typedef char yes[1];
typedef char no[2];
template<typename U>
static
yes&
test(U&&
, typename void_<decltype( std::declval<U&>().member() )>::type* = 0);
static
no&
test(...);
public:
static constexpr bool value = sizeof test(std::declval<T>()) == sizeof(yes);
};
请注意这是多么大。编写 Boost.ConceptCheck 约束类可能更容易(但请记住,SFINAE 不可重用)。
任意表达式是std::declval<U&>().member()
。在这里,要求是给定一个左值引用U
(或者T
对于特征为真的情况,如果你愿意的话),那么调用member()
它是有效的。
您还可以检查该表达式的类型(即member
为此表达式选择的任何重载的结果类型)是否可转换为类型(不要检查它是否是该类型;这太严格了,没有充分的理由)。然而,这会夸大特征,再次使这有利于约束类。
I do not know of a way to make a static_assert
part of the signature of a function template (this seems to be something you want), but it can appear inside a class template. Boost.ConceptCheck doesn't support that either.