Специализация шаблонной функции-члена шаблонного класса в GCC 7 - PullRequest
0 голосов
/ 10 сентября 2018

Я не могу скомпилировать следующий код с GCC 7.3:

    template <class C>
    class BasicScalarFormatter
    {
    public:

        typedef std::basic_string<C> String;

        template<typename T>
        static String ToString(T val)
        {
            return String{};
        }

    };

    template<class C>
    typename BasicScalarFormatter<C>::String BasicScalarFormatter<C>::ToString<bool>(bool val)
    {
        return String{};
    }

Что не так?

С VC2017 он компилируется, если ToString определен внутри класса:

    template <class C>
    class BasicScalarFormatter
    {
    public:

        typedef std::basic_string<C> String;

        template<typename T>
        static String ToString(T val)
        {
            return ToBasicString<C, T>(val);
        }

        template<>
        static String ToString(bool val)
        {
            return String{};
        }
    }

до сделать его компиляцией с GCC Я переместил ToString за пределы класса , но он все равно не компилируется.Сообщение об ошибке GCC:

ource_file.cpp:21:98: error: template-id ‘ToString<bool>’ in declaration of primary template
         typename BasicScalarFormatter<C>::String BasicScalarFormatter<C>::ToString<bool>(bool val)
                                                                                                  ^
source_file.cpp:21:50: error: prototype for ‘BasicScalarFormatter<C>::String BasicScalarFormatter<C>::ToString(bool)’ does not match any in class ‘BasicScalarFormatter<C>’
         typename BasicScalarFormatter<C>::String BasicScalarFormatter<C>::ToString<bool>(bool val)
                                                  ^
source_file.cpp:14:27: error: candidate is: template<class C> template<class T> static BasicScalarFormatter<C>::String BasicScalarFormatter<C>::ToString(T)
             static String ToString(T val)

посмотреть онлайн здесь .

Ответы [ 3 ]

0 голосов
/ 10 сентября 2018

ToString является членом шаблона другого класса шаблона.

Вы не можете специализировать внутренний шаблон, не специализируя внешний шаблон, first . То есть сначала вы должны специализировать какой-то конкретный экземпляр BasicScalarFormatter, затем взять его, и только тогда вы можете специализировать конкретный экземпляр его метода-шаблона. Но эта специализация будет распространяться, конечно, только на специализированные BasicScalarFormatter.

Ваша очевидная цель - не специализировать внешний шаблон, а иметь специализацию для этого члена класса, всех экземпляров внешнего шаблона.

Здесь нет действительно чистых решений, но этот общий шаблон проектирования обычно достаточно прост, чтобы умные компиляторы в конечном итоге оптимизировали дополнительное перенаправление:

#include <string>

template<typename C, typename T> class ToStringHelper {

public:

    static std::basic_string<C> helper()
    {
        return std::basic_string<C>();
    }
};

template<typename C> class ToStringHelper<C, bool> {

public:

    static std::basic_string<C> helper()
    {
        return std::basic_string<C>();
    }
};

template <class C>
class BasicScalarFormatter
{
public:

        typedef std::basic_string<C> String;

        template<typename T>
        static String ToString(T val)
        { 
            return ToStringHelper<C,T>::helper();
        }
};

void foo()
{
    BasicScalarFormatter<char> c;

    c.ToString(0);

    c.ToString(true);
}

Итак, в этот момент вы попадаете в статический метод вспомогательного класса. Ваша конечная цель, очевидно, состоит в том, чтобы использовать что-то, что является частью исходного шаблона. Ну, вы всегда можете передать this этому helper() и заставить его что-то с ним сделать или использовать его для вызова метода вызывающего класса.

И затем, как я уже сказал, надеюсь, что ваш компилятор избавится от этого дополнительного уровня косвенности, возможно, с некоторыми inline ключевыми словами, разбросанными здесь и там, чтобы поощрять его, или использовать грубую силу и использовать расширение вашего компилятора для заставить его все встроить.

Протестировано с gcc 8.

0 голосов
/ 10 сентября 2018

Вам не хватает template <typename C>, поскольку вы реализуете метод класса с параметром шаблона C.

Попробуй так:

#include <string>

template <class C>
class BasicScalarFormatter
{
public:

    typedef std::basic_string<C> String;

    template<typename T>
    static String ToString(T val);

};

template <typename C>
template <typename T>
typename BasicScalarFormatter<C>::String BasicScalarFormatter<C>::
ToString(T val)
{
    return String{};
}

, который должен компилироваться с GCC 7.3, см. здесь

0 голосов
/ 10 сентября 2018

Вам не хватает одного ключевого слова template, поскольку Вы определили шаблон функции внутри шаблона класса. Это должно работать:

template<class C>
template<>
typename BasicScalarFormatter<C>::String BasicScalarFormatter<C>::ToString<bool>(bool val)
{
    return String{};
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...