【问题标题】:Problems with conditional type-trait Universal Reference条件类型特征通用参考的问题
【发布时间】:2015-02-24 23:39:24
【问题描述】:

所以我有一个函数用于检查值并在值无效时抛出异常,否则在收到值时将其传回。我试图用普遍的敬意和类型特征来概括这个例程。我觉得我很接近,因为我的例子在某些情况下有效,但不是全部。它似乎只适用于右值。

#include <iostream>
#include <utility>
#include <type_traits>
#include <string>
#include <vector>

using namespace std;

struct error_type{};

template <class T>
class has_empty
{
   template<class U, class = typename std::enable_if<std::is_member_pointer<decltype(&U::empty)>::value>::type>
      static std::true_type check(int);
   template <class>
      static std::false_type check(...);
public:
   static constexpr bool value = decltype(check<T>(0))::value;
};

template <bool invalid>
struct valid {};


template <>
struct valid<false>
{
   template<typename U, typename E>
   static inline U&& check(U&& toCheck, const E& toThrow)
   {
      if (!toCheck)
        std::cout << "NoEmpty Throw" << '\n';
      else
        std::cout << "NoEmpty valid" << '\n';
      return std::forward<U>(toCheck);
   }
};

template <>
struct valid<true>
{
   template<typename U, typename E>
   static inline U&& check(U&& toCheck, const E& toThrow)
   {
      if (toCheck.empty())
        std::cout << "HasEmpty Throw" << '\n';
      else
        std::cout << "HasEmpty valid" << '\n';
      return std::forward<U>(toCheck);
   }
};

template<typename T
   , typename E
   , typename  = typename std::enable_if<std::is_base_of<error_type, E>::value>::type>
   inline T&& do_check(T&& toCheck, const E& toThrow)
{
   return valid<has_empty<T>::value>::check(std::forward<T>(toCheck), toThrow);
}

struct HasEmpty
{
    bool empty() {return false;}
};

struct NoEmpty
{
};


int main()
{
    error_type e;

    cout << has_empty<std::wstring>::value << '\n';
    cout << has_empty<std::vector<std::wstring>>::value << '\n';
    cout << has_empty<int>::value << '\n';
    cout << has_empty<HasEmpty>::value << '\n';
    cout << has_empty<NoEmpty>::value << '\n';

    do_check(true, e);
    do_check(false, e);

    do_check(std::string("45"), e);
    do_check(HasEmpty(), e);
    do_check(std::vector<bool>(), e);
    HasEmpty he;
    do_check(std::move(he), e);
    //do_check(he, e); // does not work, has_empty<T>::value is 0
}

产生输出

1
1
0
1
0
NoEmpty valid
NoEmpty Throw
HasEmpty valid
HasEmpty valid
HasEmpty Throw
HasEmpty valid

如果我取消注释最后一行,我会收到以下错误:

prog.cpp: In instantiation of 'static T&& valid<false, T, E>::check(T&&, const E&) [with T = HasEmpty&; E = error_type]':
prog.cpp:56:84:   required from 'T&& do_check(T&&, const E&) [with T = HasEmpty&; E = error_type; <template-parameter-1-3> = void]'
prog.cpp:87:19:   required from here
prog.cpp:30:11: error: no match for 'operator!' (operand type is 'HasEmpty')
       if (!toCheck)
           ^
prog.cpp:30:11: note: candidate is:
prog.cpp:30:11: note: operator!(bool) <built-in>
prog.cpp:30:11: note:   no known conversion for argument 1 from 'HasEmpty' to 'bool'

看起来has_empty&lt;T&gt;::value 正在评估false。我确信我可以做一些不同的工作来让它发挥作用,所以在这一点上它是一种学术性的。不过,我们将不胜感激。

【问题讨论】:

    标签: c++ templates c++11 universal-reference forwarding-reference


    【解决方案1】:

    当您将左值传递给do_check 时,在您的示例中为deduces T as HasEmpty&amp;。当然,引用类型没有名为 empty 的成员函数,并且表达式 decltype(&amp;U::empty) 格式不正确,导致 static std::true_type check(int) 重载导致 SFINAE 退出。

    如果你替换

    static constexpr bool value = decltype(check<T>(0))::value;
    

    static constexpr bool value = decltype(check<typename std::remove_reference<T>::type>(0))::value;
    

    所以U 永远不是引用类型,您的代码可以按预期工作。

    Live demo

    【讨论】:

    • 谢谢!这就像一个魅力。我仍然对为什么感到困惑,我知道类型被推断为引用,这是需要的,但为什么引用没有函数成员?另外,感谢您的快速响应。
    • @Apeiron 只有类类型有成员函数,引用类型没有。不知道怎么解释比较好,也许these error messages会帮助你理解。
    • 他的上下文中的引用是否像指针一样工作?我知道指针只是一个内存地址,但我一直认为引用只是别名,并且出于所有意图和目的与非引用相同。听起来引用的类型特征更接近于指针而不是类。谢谢你的解释,很有帮助。
    • 是的,指针类比似乎是正确的(当然,一般不要将指针和引用混为一谈)。引用类型的对象与原始对象相同,对其执行的成员访问与对原始对象执行的成员访问相同。但是,引用类型不同于非引用类型。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-30
    • 2018-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多