做不是使用enable_if
来执行您的要求。使用enable_if
将使函数'消失',这可能会让用户感到困惑。典型症状是错误消息,如error: no matching function for call to expression
。这并没有向用户确切地传达违反了要求。
您应该改为使用static_assert
来强制您的要求,假设C++ 0x。如果您使用的是C++ 03,那么您是否应该使用static_assert
(例如Boost的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"
现在想象你使用清晰明确的消息n你的断言,如模板foo
内部的foo: parameter type must be CopyConstructible
。因此,SFINAE和static_assert
有点对立,因此既有明确的约束违规消息,又有巧妙的过载并不总是可能的和/或容易的。
你想要做的事情很容易实现使用Boost.ConceptCheck。但是它需要写出非线性代码:约束类。我也不认为它在可用的地方使用static_assert
,所以错误消息可能不会那么好。这可能会在未来发生变化。
另一种可能性是使用static_assert
+类型特征。这种方法的有趣之处在于,使用C++ 0x时,库带有一系列有用的特性,您可以直接使用,而无需编写外部代码。更有意思的是,特征的使用并不局限于书写限制,它们也可以与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
超载已经提取了该表达式的结果类型)可转换为A型(不检查是否是那种类型,这太限制没有很好的理由)。然而,这会使这个特性膨胀,再次使这个变得更受限制。
我不知道如何使static_assert
函数模板的签名的一部分(这似乎是你想要的东西),但它可以出现在类模板中。 Boost.ConceptCheck也不支持这一点。
你可以设置某种typetrait来测试类是否有给定的成员函数,但是如果你尝试传递一个无效的模板参数,那么你得到的所有东西都是编译时错误,案子。那么为什么要麻烦。 –
我不明白使用接口类的问题是什么...... –
出于兴趣,为什么这需要是模板方法? –