Почему специализации шаблонов функций не разрешены внутри класса?

После того, как я нашел ответы на многие свои вопросы о stackoverflow, я столкнулся с вопросом, на который я не могу найти ответа, и я надеюсь, что кто-то захочет мне помочь!

Моя проблема в том, что я хочу сделать явную шаблонизацию функции внутри класса на C ++. Мой компилятор (g ++) и взгляд на стандарт C ++ (§14.7.3) говорят мне, что эта специализация должна выполняться в пространстве имен, в котором объявлен класс. Я понимаю, что это означает, что я не могу поместить специализацию внутри класса, но я не вижу смысла в этом ограничении! Кто-нибудь знает, есть ли веская причина не позволять делать специализации внутри класса?

Я знаю, что есть обходные пути, например чтобы поместить функцию в структуру, но я хочу понять, почему язык имеет такой дизайн. Если есть веская причина не разрешать использование специализированных функций внутри класса, я думаю, мне следует знать об этом, прежде чем пытаться ее обойти.

Заранее спасибо!


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

#include <cstdio>

namespace MalinTester {

template <size_t DIMENSIONALITY>
class SpecializationTest {
public:
    SpecializationTest() {
        privateVariable = 5;
    };
    virtual ~SpecializationTest() {};

    void execute() {
        execute<DIMENSIONALITY>();
    };

private:
    int privateVariable;
    template <size_t currentDim>
    static void execute() {
        printf("This is the general case. Current dim is %d. The private variable is %d.\n", currentDim, privateVariable);
        execute<currentDim-1>();
    }

    template <>
    static void execute<0>() {
        printf("This is the base case. Current dim is 0.\n");
    }

};

Это невозможно; g ++ говорит:

SpecializationTest_fcn.h:27: error: explicit specialization in non-namespace scope ‘class MalinTester::SpecializationTest<DIMENSIONALITY>’
SpecializationTest_fcn.h:28: error: template-id ‘execute<0>’ in declaration of primary template

Если я помещу функцию execute вне класса в пространство имен MalinTester, это будет выглядеть так:

#include <cstdio>

namespace MalinTester {

    template <size_t DIMENSIONALITY> class SpecializationTest {};

    template <size_t currentDim>
    void execute() {
        printf("This is the general case. Current dim is %d. The private variable is %d.\n", currentDim, privateVariable);
        execute<currentDim-1>();
    }

    template <>
    void execute<0>() {
        printf("This is the base case. Current dim is 0.\n");
    }

    template <size_t DIMENSIONALITY>
    class SpecializationTest {
    public:
        SpecializationTest() {};
        virtual ~SpecializationTest() {};

        void execute() {
            MalinTester::execute<DIMENSIONALITY>();
        };
    private:
        int privateVariable = 5;
    };
};
};

и я не могу использовать частную переменную в шаблонных версиях выполнения, поскольку она является частной в классе. Я действительно хочу конфиденциальность, так как хочу, чтобы мои данные были максимально инкапсулированы.

Конечно, я могу отправить privateVariable в качестве аргумента функции, но я думаю, что было бы лучше избежать этого, и мне действительно интересно, есть ли у стандарта C ++ веская причина не разрешать явную специализацию, как в первом пример кода выше.


@Arne Mertz: Это обходной путь, который я пробовал, но он также не позволяет использовать privateVariable. И больше всего мне интересно, стоит ли это делать. Поскольку мне не разрешено делать специализацию функций-членов, возможно, мне также не следует делать специализации функций, инкапсулированных в структуры внутри класса.

#include <cstdio>

namespace MalinTester {

template <size_t DIMENSIONALITY>
class SpecializationTest {
public:
    SpecializationTest() {
        privateVariable = 5;
    };
    virtual ~SpecializationTest() {};

    void execute() {
        Loop<DIMENSIONALITY, 0>::execute();
    };

private:
    int privateVariable;

    template <size_t currentDim, size_t DUMMY>
    struct Loop {
        static void execute() {
            printf("This is the general case. Current dim is %d.\n", currentDim);
            Loop<currentDim-1, 0>::execute();
        }
    };

    template <size_t DUMMY>
    struct Loop<0, DUMMY> {
        static void execute() {
            printf("This is the base case. Current dim is 0.\n");
        }
    };
};
};

person Malin    schedule 02.09.2013    source источник
comment
Думаю, актуально. stackoverflow.com/questions/18294990/   -  person Rapptz    schedule 02.09.2013
comment
Какую именно проблему нужно обойти? Разве нельзя просто указать специализацию в пространстве имен?   -  person n. 1.8e9-where's-my-share m.    schedule 02.09.2013
comment
n.m .: Я хочу использовать переменные экземпляра класса в функциях. Если я помещаю специализацию в пространство имен вне класса, функция не будет иметь к ним доступа (если я не отправлю экземпляр фактического класса в качестве аргумента функции, но, на мой взгляд, это сделает код более уродливым. )   -  person Malin    schedule 02.09.2013
comment
jrok: Я не уверен, что понимаю, что вы имеете в виду. Я хочу, чтобы функция-член была шаблонной, но это кажется невозможным, поскольку я не могу объявить шаблонную функцию-член внутри класса.   -  person Malin    schedule 02.09.2013
comment
@Malin Кто сказал, что нельзя использовать переменные экземпляра? См. это.   -  person jrok    schedule 02.09.2013
comment
В функции за пределами класса у меня нет доступа к переменным экземпляра. Я не хочу объявлять их общедоступными, так как хочу, чтобы данные были максимально инкапсулированы.   -  person Malin    schedule 02.09.2013
comment
@jrok Согласен. Аналогично этому. Или, может быть, мне не хватает подозрительного ограничения OP.   -  person WhozCraig    schedule 02.09.2013
comment
Можете показать код, к которому нет доступа? Вероятно, с этим возникла какая-то проблема, потому что специализация шаблона функции-члена действительно имеет доступ к другим членам его включающего класса.   -  person Angew is no longer proud of SO    schedule 02.09.2013
comment
Вы неправильно понимаете, что говорится в стандарте. У вас есть доступ. Попробуйте написать код, если он не компилируется, задайте вопрос и покажите код.   -  person n. 1.8e9-where's-my-share m.    schedule 02.09.2013
comment
Предоставьте код, описывающий, что вы хотели бы сделать, и как выглядит обходной путь (который, по-видимому, не работает).   -  person Arne Mertz    schedule 02.09.2013
comment
Я думаю, что причина запрета явной специализации шаблонов членов внутри определения шаблона включающего класса - это та же самая причина, по которой вы не можете явно специализировать шаблоны членов неспециализированных шаблонов классов в области пространства имен (т. Е. template<size_t D> template<> void SpecializationTest<D>::execute<0>(); это запрещено) [temp.expl.spec] / 16.   -  person dyp    schedule 02.09.2013
comment
Цитата из текста вопроса: @Arne Mertz: Это обходной путь, который я пробовал, но он также не позволяет использовать privateVariable. Я думаю, что проблема здесь в неправильном понимании объема статических функций-членов. В таких функциях отсутствует неявный указатель на экземпляр включающего класса, иначе говоря this, это означает, что он НИКОГДА не может неявно получить доступ к любому члену своего класса, публичному или нет, без ссылки на объект этого класса. . Функция-член дает ей доступ ко всем ее членам при условии, что у нее есть доступ к ссылке на экземпляр этого класса.   -  person brunocodutra    schedule 02.09.2013
comment
Почти дубликат этого вопроса stackoverflow.com/questions/2537716/, разница в том, что в этом случае мы имеем дело с функциями, которые, тем не менее, работают точно так же, как типы для что здесь важно, помимо дальнейшего упрощения, что шаблоны функций не допускают частичной специализации, в то время как типы позволяют.   -  person brunocodutra    schedule 02.09.2013
comment
@DyP: я думаю, что причина запрета явной специализации шаблонов элементов в определении шаблона включающего класса - та же самая причина, по которой вы не можете явно специализировать шаблоны членов неспециализированных шаблонов классов в области пространства имен ‹- ОК, и почему это запрещено , тогда? :-П   -  person Malin    schedule 03.09.2013
comment
@brunocodutra: Я знаю, но поскольку это самая внутренняя часть кода, критичного к производительности, я не хочу создавать экземпляры структур, если они не нужны. (Я знаю, было бы неплохо сделать так, чтобы общедоступный метод также выполнялся статическим, чтобы избежать создания экземпляра класса и отправить частную переменную в качестве аргумента, но это следующий шаг. Теперь я хочу понять, почему в стандарте C ++ это, на мой взгляд, ограничение.)   -  person Malin    schedule 03.09.2013
comment
@brunocodutra Я не видел обсуждения, о котором вы говорите, спасибо. Вы имеете в виду, что причина просто в том, чтобы облегчить жизнь разработчикам компиляторов?   -  person Malin    schedule 03.09.2013
comment
@Malin Часть логического обоснования, которое я имел в виду, дается ответом на вопрос, связанный с brunocodutra: явно специализированный шаблон функции это функция, а не шаблон; но шаблон явно специализированной функции внутри не полностью специализированного шаблона класса все равно должен был быть шаблоном. Это не совсем касается авторов компилятора, поскольку правила создания экземпляров для членов шаблонов шаблонов классов сильно отличаются от явных специализаций этих членов (т.е. разрешение этого приведет к более сложным правилам).   -  person dyp    schedule 03.09.2013
comment
@Malin Я верю в это, но это не такой простой вопрос, как кажется, потому что нет смысла определять сложное поведение, которое ни один разработчик компилятора не сможет реализовать. Это произошло с ключевым словом export и связанным с ним поведением: в течение многих лет ни один крупный компилятор не реализовал его, до такой степени, что комитет проголосовал за его исключение, когда проголосовал за стандарт С ++ 11. Хотя я не знаю наверняка, применимо ли здесь то же самое обоснование, я думаю, что, по крайней мере, справедливо учитывать сложность реализации при голосовании за новую функцию.   -  person brunocodutra    schedule 04.09.2013
comment
Хорошо, в этом есть смысл. Спасибо вам обоим!   -  person Malin    schedule 04.09.2013


Ответы (1)


Базовая специализация:

In .h:

template <class T>
class UISelectorSlider :  public UISelectorFromRange<T> {
public:
    UISelectorSlider();
    virtual ~UISelectorSlider();
private:
    float width;
    float getPositionFromValue(T value);
};

В .cpp в том же пространстве имен:

template <>
float UISelectorSlider<MVHue>::getPositionFromValue(MVHue value)
{
    return width * (float)value / 360.0;
}

Если вам нужна специализированная функция в специализированном классе:

Внутри класса add (.h) (закрытая функция):

private:
    template <int I>
    void foo();

Специализация внутри .cpp:

template <>
template <>
void UISelectorSlider<MVHue>::foo<3>()
{
     // you can access private fields here
}

ОБНОВИТЬ:

Но нельзя написать что-то подобное:

template <class T>
template <>
void UISelectorSlider<T>::foo<3>()
{
     // you can access private fields here
}

Вы получите: error: шаблоны включающих классов не являются явно специализированными.

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

person Victor Laskin    schedule 03.09.2013
comment
Я ценю, что люди готовы ответить, но мой вопрос: есть ли веская причина, по которой стандарт не разрешает специализацию шаблонных функций внутри класса, а не как обойти это. (Более того, я хочу инкапсулировать функцию в классе!) - person Malin; 03.09.2013