【问题标题】:Partial specialization of members成员的部分专业化
【发布时间】:2017-09-24 17:36:58
【问题描述】:

尝试专门化成员方法。
阅读上一个问题:std::enable_if to conditionally compile a member function
我很能理解我做错了什么。

#include <string>
#include <iostream>
#include <type_traits>

template<typename T>
class Traits
{
};

struct Printer
{
    template<typename T>
    typename std::enable_if<!std::is_function<decltype(Traits<T>::converter)>::value, void>::type
    operator()(T const& object)
    {
        std::cout << object;
    }
    template<typename T>
    typename std::enable_if<std::is_function<decltype(Traits<T>::converter)>::value, void>::type
    operator()(T const& object)
    {
        std::cout << Traits<T>::converter(object);
    }
};

template<>
class Traits<std::string>
{
    public:
        static std::size_t converter(std::string const& object)
        {
            return object.size();
        }
};

int main()
{
    using namespace std::string_literals;

    Printer     p;

    p(5);
    p("This is a C-string");
    p("This is a C++String"s);  // This compiles.
}

编译给出:

> g++ -std=c++1z X.cpp
X.cpp:42:5: error: no matching function for call to object of type 'Printer'
    p(5);
    ^
X.cpp:14:5: note: candidate template ignored: substitution failure [with T = int]: no member named 'converter' in 'Traits<int>'
    operator()(T const& object)
    ^
X.cpp:20:5: note: candidate template ignored: substitution failure [with T = int]: no member named 'converter' in 'Traits<int>'
    operator()(T const& object)
    ^

他们似乎都失败了,因为他们看不到方法converter。但我正在尝试使用 SFINE 和 std::enable_if 来识别此函数不存在,因此仅实例化其中一种方法。

每种类型都会产生相同的错误:

X.cpp:43:5: error: no matching function for call to object of type 'Printer'
    p("This is a C-string");
    ^
X.cpp:14:5: note: candidate template ignored: substitution failure [with T = char [19]]: no member named 'converter' in 'Traits<char [19]>'
    operator()(T const& object)
    ^
X.cpp:20:5: note: candidate template ignored: substitution failure [with T = char [19]]: no member named 'converter' in 'Traits<char [19]>'
    operator()(T const& object)
    ^

注意:它为std::string 版本编译。

【问题讨论】:

    标签: c++ enable-if


    【解决方案1】:

    您可以遵循私有辅助函数,并使用重载解决方案来选择积极的 SFINAE-d 重载 - 而不是消极的 SFINAE-d 重载:

    struct Printer
    {
        template <class T>
        void operator()(T const& object) {
            call_impl(object, 0);
        }
    
    private:
        // selected if Traits<T>::converter exists and is a function
        // preferred in this case because int is better than ...
        template<typename T>
        typename std::enable_if<std::is_function<decltype(Traits<T>::converter)>::value, void>::type
        call_impl(T const& object, int)
        {
            std::cout << Traits<T>::converter(object);
        }
    
        // selected if either Traits<T>::converter doesn't exist or isn't a function
        template<typename T>
        void call_impl(T const& object, ...)
        {
            std::cout << object;
        }
    
    };
    

    我们将在 C++2a 中使用约束函数获得的好处之一是我们可以在没有额外帮助器的情况下做到这一点:

    struct Printer
    {
        template <class T>
            requires std::is_function<decltype(Traits<T>::converter)>::value
        void operator()(T const& object)
        {
            std::cout << Traits<T>::converter(object);
        }
    
        template <class T>
        void operator()(T const& object)
        {
            std::cout << object;
        }
    };
    

    【讨论】:

      【解决方案2】:

      问题在于SFINAE 的工作方式。当替换失败时,整个函数被从程序中取出。因此,即使您的谓词 typename std::enable_if&lt;!std::is_function&lt;decltype(Traits&lt;T&gt;::converter)&gt;::value, void&gt;::type 旨在捕捉 false 的情况,converter 的不存在也会导致重载被取消。

      最简单的解决方法是这样的:

      struct Printer
      {
          template<typename T>
          void
          impl(T const& object, ...)
          {
              std::cout << object;
          }
      
          template<typename T>
          typename std::enable_if<std::is_function<decltype(Traits<T>::converter)>::value, void>::type
          impl(T const& object, void*)
          {
              std::cout << Traits<T>::converter(object);
          }
      
          template<typename T>
          void
          operator()(T const& x)
          {
              return impl(x, nullptr);
          }
      };
      

      基本上:您为编译器提供了无需使用谓词即可始终工作的东西。这里的技巧是nullptr 将匹配到void* 而不是...,所以它会做你想做的事。

      如果您想从中获得真正的乐趣,您可以创建一个返回类型为true_typefalse_typehas_converter 函数并在其上重载实现。

      struct Printer
      {
          template<typename T>
          std::false_type
          has_converter(T const& object, ...);
      
          template<typename T>
          typename std::enable_if<std::is_function<decltype(Traits<T>::converter)>::value, std::true_type>::type
          has_converter(T const& object, void*);
      
          template<typename T>
          void impl(T const& x, std::false_type)
          {
              std::cout << x;
          }
      
          template<typename T>
          void impl(T const& x, std::true_type)
          {
              std::cout << Traits<T>::converter(x);
          }
      
          template<typename T>
          void
          operator()(T const& x)
          {
              return impl(x, decltype(has_converter(x, nullptr))());
          }
      };
      

      可以想象一个辅助函数或模板化 constexpr bool 以使使用此属性更加容易(使用与上述相同的技术)。

      template <typename T>
      constexpr bool has_converter = ???;
      

      【讨论】:

        【解决方案3】:

        将非函数converter 添加到非专业特征怎么样?

        template<typename T>
        class Traits
        {
            public: enum class Dummy{nothing};
            public: static Dummy const converter = Dummy::nothing;
        };
        

        Run this code online

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-07-26
          • 1970-01-01
          • 1970-01-01
          • 2022-01-24
          相关资源
          最近更新 更多