шаблонен параметър без тип от дефиниран от потребителя тип

Опитвам се да дефинирам шаблонен клас, който има шаблонен параметър без тип от дефиниран от потребителя тип. За съжаление засега без успех. Истинският код е твърде дълъг, но опростен пример изглежда така:

#include <iostream>

template <class T>
class Maybe {
    bool is_ = false;
    T value_;

  public:
    constexpr Maybe() = default;
    constexpr Maybe(T value) : is_(true), value_(value) {}

    constexpr bool is() const { return is_; }
};

template <Maybe<int> parm>
struct Test {
    void say() const {
        std::cout << "parm is " << (parm.is() ? "set" : "not set") << ".\n";
    }
};

int main() {
    Test<Maybe<int>{}> not_set;
    Test<Maybe<int>(2)> is_set;

    not_set.say();
    is_set.say();
}

Когато се опитвам да компилирам този код (с Clang 3.4), получавам следното съобщение за грешка:

test.cc:15:22: error: a non-type template parameter cannot have type
      'Maybe<int>'
template <Maybe<int> parm>
                     ^
test.cc:23:10: error: value of type 'Maybe<int>' is not implicitly
      convertible to 'int'
    Test<Maybe<int>{}> not_set;
         ^~~~~~~~~~~~
test.cc:24:10: error: value of type 'Maybe<int>' is not implicitly
      convertible to 'int'
    Test<Maybe<int>(2)> is_set;
         ^~~~~~~~~~~~~
3 errors generated.

Сега знам, че параметър на шаблон без тип трябва да отговаря на някои условия. Мислех обаче, че да съм constexpr би било достатъчно. Или наистина може да е само един от вградените интегрални типове?

Има ли начин да предам параметри на шаблон без тип от моя собствен дефиниран от потребителя тип?


person Lemming    schedule 28.08.2014    source източник


Отговори (4)


Целият този проблем е самата причина, поради която рационалните числа са имплементирани като шаблонен клас с действителните числа в параметрите на шаблона. за да получите шаблонен параметър, съхраняващ повече от просто int, трябва да създадете подобен клас като std::ratio, който също се оценява само по време на компилация.

що се отнася до вашия действителен пример, помислете дали да не напишете нещо подобно на:

template<class T, T ... Params>
class Maybe {

и тогава

Test<Maybe<int,5> > is_set;

or

Test<Maybe<int> > not_set;
person user45927    schedule 28.08.2014
comment
Това е наистина хубаво приложение на различни шаблони. Обмислях нещо от формата template<class T, bool is=false, T value={}> class Maybe{};. Но вашият подход е много по-добър. - person Lemming; 29.08.2014

Не, не можеш.

n3376 14.1/7

Шаблон-параметър, който не е тип, не трябва да бъде деклариран като тип с плаваща запетая, клас или void.

template<double d> class X; // error
template<double* pd> class Y; // OK
template<double& rd> class Z; // OK

така че можете да подадете указател или препратка, но не и обект от тип клас.

пример на живо

person ForEveR    schedule 28.08.2014
comment
Благодаря за пояснението. Жалко, че това не е възможно. - person Lemming; 28.08.2014

Знам, че този въпрос е стар, но бих искал да посоча шаблонен подход за мета програмиране към него.

В c++ можете да подадете всеки тип към шаблон като този, така че защо просто не го направите тип?

За да направите това, ще искате да направите клас обвивка за вашия клас Maybe:

template <typename T>
struct maybe_wrap 
{
    Maybe<T> value{};
}

template<typename T, T value>
struct maybe_wrap
{
    Maybe<T> value{value};
}

След това просто предайте maybe_wrap като име на тип и просто осъществете достъп до maybe_wrap<int, 3>().value от него, когато имате нужда от него!

Единственото ограничение за това е, че T може да не е една от стойностите без тип (int, bool и т.н.).

В такъв случай просто използвайте горната логика отново!

person Russell Greene    schedule 14.07.2015

В C++20 това е възможно с помощта на типове класове на структурни литерали:

#include <iostream>

struct NullOptT {} NullOpt;

/**
 * Literal class type.
 *
 * Represents an optionally provided `int`.
 */
struct OptionalInt {
    constexpr OptionalInt(NullOptT) {}
    constexpr OptionalInt(int value): has_value(true), value(value) {}

    const bool has_value = false;
    const uint32_t value {};
};

/**
 * Prints whether or not a value was provided for "maybe" WITHOUT branching :)
 */
template<OptionalInt maybe>
void Print() {
    if constexpr(maybe.has_value) {
        std::cout << "Value is: " << maybe.value << std::endl;
    } else {
        std::cout << "No value." << std::endl;
    }
}

// Note: implicit conversions are at play!
int main()
{
    Print<123>();     // Prints "Value is: 123"
    Print<NullOpt>(); // Prints "No value."
}

Написах публикация в блог с повече подробности относно използването и ограниченията на класовите литерали NTTP тук: Литерални класове като нетипови шаблонни параметри в C++20.

person Kevin Hartman    schedule 15.02.2020