成员的部分专业化

Partial specialization of members

正在尝试专门化成员方法。
阅读上一个问题: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 版本。

将非功能 converter 添加到非专业特征如何?

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

Run this code online

您可以使用私有辅助函数,并使用重载决策来优先于正 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;
    }
};

问题在于 SFINAE 的工作方式。当替换失败时,整个函数就从程序中取出。因此,即使您的谓词 typename std::enable_if<!std::is_function<decltype(Traits<T>::converter)>::value, void>::type 旨在捕获 false 情况,converter 的不存在也会导致重载从 table.[=24 中移除=]

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

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* 而不是 ...,所以它会做你想做的事。

如果你想从中获得真正的乐趣,你可以创建一个 has_converter 函数,其 return 类型为 true_typefalse_type 并重载其实现.

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 = ???;