2011-08-28 78 views
2

打字有没有办法做这样的事情在下面的C++C++编译时鸭接口

template<typename TAnimal> 
bool can_eat(TAnimal& animal) where bool TAnimal::IsAlive() exists 
{ 
    return !animal.IsAlive(); 
} 

//... 

Duck duck; 
assert(can_eat(duck) == true); //compiles 

Wood wood; 
can_eat(wood); // fails to compile because wood doesn't have IsAlive() 

显式接口,在我看来,使它更加清晰的功能所期待的。但是,我不想创建一个实际的接口类(非常乏味)。

+1

你可以设置某种typetrait来测试类是否有给定的成员函数,但是如果你尝试传递一个无效的模板参数,那么你得到的所有东西都是编译时错误,案子。那么为什么要麻烦。 –

+2

我不明白使用接口类的问题是什么...... –

+0

出于兴趣,为什么这需要是模板方法? –

回答

8

不是使用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也不支持这一点。

+0

好的一般介绍。我更喜欢它,如果game_(Concepts)的_name稍早提到,虽然 – sehe

+0

@sehe啊;但这不是概念,而是次佳。我没有解决这些问题,因为另一个答案提到他们。一个关键的区别是,C++ 0x概念检查/ SFINAE最好根据有效表达式来完成。如果我理解正确,概念就处于更高层次。 –

0

你不需要做任何事情 - 只要在你的例子中省略假设的“where ... exists”,它就是正常的C++代码。

如果你坚持只有某些条件下的功能是可用的,你可以尝试用has_member从这里结合boost::enable_ifhttp://lists.boost.org/Archives/boost/2002/03/27229.php

的想法是,你会只允许模板功能进行,如果一些条件实例遇到了......但由于SFINAE编译器基本上已经在为条件与该函数的实际编译时需求(如您的示例中)相同的情况下为您执行该操作。

1
where void TAnimal::IsAlive() exists 

我假设你的意思是bool TAnimal::IsAlive()?如果是这样,C++已经做了你所要求的。如果鸭子进行IsAlive()方法,那么这将编译:

Duck duck; 
assert(can_eat(duck) == true); //compiles 

如果伍德不具备的IsAlive()方法,这不会编译:

Wood wood; 
can_eat(wood); // fails to compile because wood doesn't have IsAlive() 

这就是你在问什么对吗?

+0

对不起,应该是bool ... – jameszhao00

+0

感谢您的回答。有没有办法将这些需求放在一个地方(例如在函数decl处),而不是在整个代码中进行intersparsed? – jameszhao00

+1

当然,但海事组织比它的价值更麻烦。你需要一个类型特征,并且你必须为每个需要测试的方法定义一个不同的特征,你不能只有一个处理它们的通用方法。查看[这个问题]的答案(http://stackoverflow.com/questions/257288/is-it-possible-to-write-ac-template-to-check-for-a-functions-existence),找到了解如何做到这一点。 –

5

这是概念打算解决的事情。

概念是对最新C++标准的增加,但因为委托人不相信它们已经足够稳固,不能包含在语言中。 See what Herb Sutter wrote关于他们被排除在标准之外。

从技术上讲,概念是不必要的,因为模板只是使用任何他们可以(即丢失where条款,并且你有你所要求的)。如果在编译时所需的方法不存在,那么代码就不会编译。但是概念会给编码器更加明确地控制类型的接口,并且会给出比大多数编译器当前提供的更合理的错误消息。

0

正如其他人所说,这只会工作。如果函数不存在,它将无法实例化模板。

boost库包含一些类来协助这种事情,例如enable_if它可以用来在条件为真的情况下仅“启用”模板。还有一些相关的type traits库,您可能可以使用this在编译时确定要调用的函数是否存在。

我不得不承认,我没有使用任何的这个自己,但它看起来像你对我应该能够用它来实现你想要的...

2

加速提供BOOST_STATIC_ASSERT这一点。刚刚批准的C++标准版本将提供一个static_assert宏的内置版本。

enable_if另一方面,并​​不是真的很适合这个。它可以使用可以使用,但enable_if的主要目的是区分否则模糊的重载。