Шаблонный конструктор по умолчанию

Я хотел бы, чтобы определение конструктора по умолчанию переключалось во время компиляции с помощью параметра шаблона. Я могу заставить это скомпилировать OK для конструктора преобразования, но пытаюсь использовать этот метод для конструктора по умолчанию, чтобы он был по умолчанию или нет - полезно, если в случае определенного параметра шаблона результирующий класс может быть POD, но в другом случае , это невозможно, но при этом я получаю ошибку компилятора. За исключением специализации шаблона и дублирования всего кода, есть ли способ сделать это? Вот упрощенная версия того, что я пытался:

#include<type_traits>   // for enable_if

template <bool MyParameter>
class Demonstration
{
    public:

        //trivial copy, move constructors/assignment, and trivial destructor
        constexpr Demonstration(Demonstration const &) = default;
        constexpr Demonstration(Demonstration &&) = default;
        Demonstration & operator= (Demonstration const &) = default;
        Demonstration & operator= (Demonstration &&) = default;
        ~Demonstration() = default;

        // this one gives "error: a template cannot be defauled"
        template <bool Dummy=MyParameter, typename std::enable_if< Dummy , bool >::type=true >
        Demonstration() = default;

        // ok
        template <bool Dummy=MyParameter, typename std::enable_if< !Dummy , bool >::type=false >
        Demonstration() : myValue(0) {}

        // ok
        template <bool Dummy=MyParameter, typename std::enable_if< Dummy , bool >::type=true >
        explicit constexpr Demonstration(unsigned char toConvert)
        : myValue ( toConvert )
        {
        }
        // ok
        template <bool Dummy=MyParameter, typename std::enable_if< !Dummy , bool >::type=false >
        explicit constexpr Demonstration(unsigned char toConvert)
        : myValue ( toConvert > 100 ? 0 : toConvert )
        {
        }

    // a lot of functions that do not depend on parameter go here

    protected:
    private:
        unsigned char myValue;

};

person Signifying Nothing    schedule 06.04.2014    source источник
comment
Даже если это скомпилировано, Demonstration<true> технически все равно не будет POD. Тривиальный класс — это класс, который имеет конструктор по умолчанию, не имеет нетривиальных конструкторов по умолчанию и может быть легко скопирован. Demonstration<true> имеет два конструктора по умолчанию и один нетривиальный, хотя он никогда не может быть жизнеспособной функцией для разрешения перегрузки.   -  person aschepler    schedule 06.04.2014
comment
CRTP реализует дублирующие функции, и есть ли у ctors две специализации?   -  person Yakk - Adam Nevraumont    schedule 06.04.2014
comment
Вы не можете использовать конструктор шаблона по умолчанию, потому что вы не можете явно указать его аргументы шаблона. Вы можете создавать конструкторы шаблонов только с параметрами, которые позволяют компилятору выводить аргументы шаблона. Вы можете попытаться добиться того, чего хотите, с помощью статической функции шаблона, которая возвращает сконструированный объект класса Demonstration.   -  person Constructor    schedule 06.04.2014
comment
Спасибо всем, это проясняет мое замешательство. @Yakk: CRTP выглядит как способ добиться того, чтобы класс был POD или не-POD на основе параметра шаблона без большого количества дублирования; если вы представите это как ответ, я приму это.   -  person Signifying Nothing    schedule 07.04.2014
comment
@signifyingnothing нет, просто сам себе ответ: я не могу сейчас написать хороший. И плохой ответ не стоит писать.   -  person Yakk - Adam Nevraumont    schedule 07.04.2014


Ответы (1)


GCC жалуется на ваш шаблон:

error: a template cannot be defaulted

и Clang жалуется:

error: only special member functions may be defaulted.

Это кажется достаточно справедливым. Шаблон функции-члена не является функцией-членом, не говоря уже о специальной.

Вы хотели бы, чтобы Demonstration<bool P> было POD, когда P верно, а в противном случае это не обязательно так.

Возможное решение состоит в том, чтобы полностью делегировать параметризацию POD специализациям базового шаблона base<bool P> и наследовать Demonstration<P> base<P>. Вот иллюстрация:

#include<type_traits>

template<bool Param = true>
struct base // is POD 
{
    base() = default;
    explicit constexpr base(unsigned char ch)
    : _val(ch){}
    unsigned char _val;
};

template<>
struct base<false> // is not POD
{
    base() = default;
    explicit constexpr base(unsigned char ch)
    : _val(ch > 100 ? 0 : ch){}
    unsigned char _val = 0;
};


template <bool MyParameter>
class Demonstration : private base<MyParameter>
{   
public:

    Demonstration() = default;
    //trivial copy, move constructors/assignment, and trivial destructor
    constexpr Demonstration(Demonstration const &) = default;
    constexpr Demonstration(Demonstration &&) = default;
    Demonstration & operator= (Demonstration const &) = default;
    Demonstration & operator= (Demonstration &&) = default;
    ~Demonstration() = default;

    explicit constexpr Demonstration(unsigned char toConvert)
    : base<MyParameter>(toConvert)
    {
    }

    char myValue() const {
        return base<MyParameter>::_val;
    }
};


#include <iostream>

using namespace std;

int main()
{
    cout << "is_pod<base<true>>::value = " 
        << is_pod<Demonstration<true>>::value << endl;
    cout << "is_pod<base<false>>::value = " 
        << is_pod<Demonstration<false>>::value << endl;
    cout << "is_pod<Demonstration<true>>::value = " 
        << is_pod<Demonstration<true>>::value << endl;
    cout << "is_pod<Demonstration<false>>::value = " 
        << is_pod<Demonstration<false>>::value << endl;
    Demonstration<true> d_true(1);
    Demonstration<false> d_false(101);
    std::cout << "(int)Demonstration<true>(1).myValue() = " 
        << (int)d_true.myValue() << endl;
    std::cout << "(int)Demonstration<false>(101).myValue() = " 
        << (int)d_false.myValue() << endl;
    return 0;
}

Теперь Demonstration<P> — это POD на тот случай, если base<P> — это POD. Программа выводит:

is_pod<base<true>>::value = 1
is_pod<base<false>>::value = 0
is_pod<Demonstration<true>>::value = 1
is_pod<Demonstration<false>>::value = 0
(int)Demonstration<true>(1).myValue() = 1
(int)Demonstration<false>(101).myValue() = 0

Построен с помощью GCC 4.8.2 и clang 3.3.

person Mike Kinghan    schedule 11.04.2014