If/else во время компиляции?

Рассмотрим следующий код:

#include <iostream>
#include <type_traits>

template<typename T> class MyClass
{
    public:
        MyClass() : myVar{0} {;}
        void testIf() {
            if (isconst) {
                myVar;
            } else {
                myVar = 3;
            }
        }
        void testTernary() {
            (isconst) ? (myVar) : (myVar = 3);
        }

    protected:
        static const bool isconst = std::is_const<T>::value;
        T myVar;
};

int main()
{
    MyClass<double> x;
    MyClass<const double> y;
    x.testIf();
    x.testTernary();
    y.testIf(); // <- ERROR
    y.testTernary(); // <- ERROR
    return 0;
}

Для x (не const) нет проблемы. Но y (тип данных const) вызывает ошибку, даже если условие в if/else известно во время компиляции.

Есть ли возможность не компилировать ложное условие во время компиляции?

Ответ 1

Простейшим решением является частичная спецификация шаблона:

template<typename T> class MyClassBase
{
    public:
        MyClassBase() : myVar{0} {;}

    protected:
        T myVar;
};

template<typename T> class MyClass: MyClassBase<T>
{
    public:
        void testIf() { myVar = 3; }
};

template<typename T> class MyClass<const T>: MyClassBase<const T>
{
    public:
        void testIf() { myVar; }
};

Другим вариантом является делегирование:

template<typename T> class MyClass
{
    public:
        MyClass() : myVar{0} {;}
        void testIf() { testIf_impl(std::integral_constant<bool, isconst>()); }

    protected:
        static const bool isconst = std::is_const<T>::value;
        T myVar;

    private:
        void testIf_impl(std::true_type) { myvar; }
        void testIf_impl(std::false_type) { myVar = 3; }
};

SFINAE - это еще один вариант, но в этом случае обычно не рекомендуется:

template<typename T> class MyClass
{
    public:
        MyClass() : myVar{0} {;}
        template
        <typename U = void>
        typename std::enable_if<std::is_const<T>::value, U>::type testIf() { myvar; }
        template
        <typename U = void>
        typename std::enable_if<!std::is_const<T>::value, U>::type testIf() { myvar = 3; }

    protected:
        static const bool isconst = std::is_const<T>::value;
        T myVar;
};

Ответ 2

Вы можете специализировать класс для типов const

template<typename T>
class MyClass 
{
   // Whatever you need to do
};

template<typename T>
class MyClass<const T> 
{
   // Whatever you need to do for const types
};

Ответ 3

Шаблон класса скомпилирован для данного типа. Даже если поток управления не попадает на назначение, это задание также скомпилировано. Поскольку элемент const, компиляция завершится неудачей.

Вы можете использовать некоторую форму SFINAE, чтобы пропустить это задание, но он не будет работать так, как сейчас.

Это работает (я просто удалил функцию элемента testTernary):

#include <iostream>
#include <type_traits>

template<typename T> class MyClass
{
    public:
        MyClass() : myVar{0} {;}

        template<class U = T>
        typename std::enable_if<std::is_const<U>::value>::type testIf() {
            myVar;
        }

        template<class U = T>
        typename std::enable_if<!std::is_const<U>::value>::type testIf() {
            myVar = 3;
        }

    protected:
        static const bool isconst = std::is_const<T>::value;
        T myVar;
};

int main()
{
    MyClass<double> x;
    MyClass<const double> y;
    x.testIf();
    y.testIf();
    return 0;
}

Ответ 4

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

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

Ответ 5

Попробуйте следующее:

template<typename T> 
class MyClass
{
    T myVar;
public:
    MyClass() : myVar(0) {}

    void testIf()
    {
        assign(myVar, 3);
    }
 private:

    template<typename V>
    void assign(V& destination, int value)
    {
        destination = value;
    }
    template<typename V>
    void assign(const V& destination, int value)
    {

    }
};