为什么这C++检测T型是否具有空运算符(EDT const&)失败的特征?



我正在尝试使用sfinae检测是否传递为模板参数t的类型是否具有t :: operator()(p const&),其中p也是模板参数。不幸的是,在此示例的这个示例之后,我对解决方案进行了建模,即使我可以使其适用于正常方法。

这是一些示例代码,证明了我面临的问题:

#include <iostream>
#include <iomanip>
#include <utility>
#include <type_traits>
using namespace std;
struct has
{
    void operator()(int const&);    
};
struct hasNot1
{
    void operator()(int);   
};
struct hasNot2
{
    void operator()();  
};
struct hasNot3
{
    void operator()(float); 
};
struct hasNot4
{
};
template<typename T, typename EDT>
struct is_callable_oper
{
      private:
                    typedef char(&yes)[1];
                    typedef char(&no)[2];
                    template <typename U, void (U::*)(EDT const &)> struct
                                                                        Check;
                    template<typename>
                    static yes test(...);
                    template <typename U>
                    static no
                            test(Check<U, &U::operator()>*);
                public:
                    static constexpr bool value = sizeof(test<T>(0))
                                                                == sizeof(yes);
};
int main() {
    cout << boolalpha << is_callable_oper<has, int&>::value << " " 
         << is_callable_oper<has, int>::value << " "
         << is_callable_oper<hasNot1, int&>::value << " "
         << is_callable_oper<hasNot2, int&>::value << " "
         << is_callable_oper<hasNot3, int&>::value << " "
         << is_callable_oper<hasNot4, int&>::value << endl;
    return 0;
}

在IDEONE(https://ideone.com/te49xr)上运行它: true false true true true true

我期望: true false false false false false

工作完成:

  1. 阅读此stackoverflow问题。我还遵循了相关的链接。

  2. 查找std :: dectval,dectType。我还研究了非类型模板参数如何导致歧义的一些研究。我一直在使用http://en.cppreference.com/主要是。

  3. 阅读其他一些相关的问题和链接。

注意:我正在使用C 0x的GCC 4.6.3。

最终目标:检测此签名中包含的所有可呼叫功能指针。

相关的澄清:我仍然对某些概念感到困惑,如果您可以回答这些概念,我将很感激。当然,如果它们属于一个单独的问题,请告诉我。

  1. 我们可以通过使用Dectval而不是检查模板来触发Sfinae的歧义吗?

  2. 超载运算符的功能类型是什么?例如,在这种情况下,超载运算符会有以下类型: void (operator())(EDT const&)

  3. 作为CV预选赛在此检查中被丢弃,检查我通过的论点的最佳方法是什么。

  4. 我无法找到一种使用Boost做到这一点的方法。我相信,我也使用较旧的Boost版本(将检查并更新精确)卡住。如果没有理由滚动我自己的支票,那可能是最好的。

我仍然是这个领域的初学者,如果这太基本,我深表歉意。如果您可以指出我认为我也应该研究的其他资源,我将不胜感激。同时,我将继续在线搜索并尝试解决方案。


编辑1


在与@nir Friedman讨论了问题后,我开始意识到打破隐式转换规则并实现确切的匹配不是我想要的。只要可以转换传递的类型,就可以了。我将感谢有关如何实现这一目标的指针。


编辑2


我将这个问题标记为关闭,因为@SAM Varshavchik回答了我问的确切问题。如果有人对编辑1中提出的问题感兴趣,我要么将其作为一个单独的问题,要么在此处发布我的解决方案。您澄清了您的operator()返回void,并且您想严格匹配签名,忽略类型转换。

如果是这样,那么您的预期结果应为false true false false false false,而不是true false false false false false

 is_callable_oper<has, int&>::value

由于has::operator()不使用int & const &参数,该参数崩溃到int &,因此该测试的结果必须为false。

 is_callable_oper<has, int>

由于has确实确实具有operator()进行const int &参数,因此该测试应通过。

我的解决方案只需使用std::is_same比较两种类型,而std::enable_if进行SFINAE-FAIL A模板分辨率候选。

#include <type_traits>
#include <iostream>
struct has
{
    void operator()(int const&);
};
struct hasNot1
{
    void operator()(int);
};
struct hasNot2
{
    void operator()();
};
struct hasNot3
{
    void operator()(float);
};
struct hasNot4
{
};
template<typename T, typename P, typename foo=void>
class is_callable_oper : public std::false_type {};
template<typename T, typename P>
class is_callable_oper<T, P, typename std::enable_if<
         std::is_same<decltype(&T::operator()),
                      void (T::*)(const P &)>::value>::type>
    : public std::true_type {};
int main() {
    std::cout << std::boolalpha << is_callable_oper<has, int&>::value << " "
         << is_callable_oper<has, int>::value << " "
         << is_callable_oper<hasNot1, int&>::value << " "
         << is_callable_oper<hasNot2, int&>::value << " "
         << is_callable_oper<hasNot3, int&>::value << " "
          << is_callable_oper<hasNot4, int&>::value << std::endl;
    return 0;
}

编辑:通过使用std::void_t或合理的传真,在专业化中,也应该有可能与超载运算符一起工作:

template<typename T, typename P>
class is_callable_oper<T, P,
        std::void_t<decltype( std::declval< void (T::*&)(const P &)>()=&T::operator())>>
    : public std::true_type {};

在更现代的编译器上,这种问题的典型方法是void_t成语:`void_t`如何工作。这就是进展。步骤1:

template <class T>
using void_t = void;  // avoid variadics for simplicity

显然,这是一般的,不是您的问题,但直到17个标准库中才碰巧。

接下来,我们定义我们的性状,并将默认检测视为false:

template <class T, class P, class = void>
struct is_callable_oper : std::false_type {};

现在,我们定义了一个专门的表格,如果我们的功能具有我们想要的操作员,该表格将有效。

template <class T, class P>
struct is_callable_oper<T, P, 
    void_t<decltype(std::declval<T>()(std::declval<P>()))>>: std::true_type {};

基本想法是,众所周知,void_t将始终转化为void。但是,除非表达式有效,否则输入的类型将没有任何意义。否则将导致替代失败。因此,基本上,上面的代码将检查

是否
std::declval<T>()(declval<P>())

对于您的类型来说是明智的。这基本上是您想要的特征检测。请注意,有一些涉及LVALUE,RVALUES,CONSTNESS的微妙之处,但要进入这里有点涉及。

现在,正如Sam在评论中指出的那样,该表达式是否明智,包括隐式转换。因此,输出确实是真实的真实false true false。工作示例:http://coliru.stacked-crooked.com/a/81ba8b208b90a2ba。

例如,您期望is_callable_oper<has, int>::value是错误的。但是,显然可以调用使用int接受const int&的函数。因此,您将获得真实。

最新更新