Специализация/перегрузка шаблона C++

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

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

Foo<1>();
Foo<'1'>(); // different types of object

Я не думаю, что это возможно, поэтому я вынужден сделать что-то вроде этого

template <typename T, T Val>
struct Foo;

template <int Val>
struct Foo<int, Val> 
{};

template <char Val>
struct Foo<char, Val>
{};

//...

Foo<int, 1>();
Foo<char, '1'>();

так что Foo может быть специализировано на основе первого параметра шаблона. Однако это усложняет синтаксис мини-языка, который я пытаюсь реализовать в своей структуре метапрограммирования. Есть ли какая-либо техника, позволяющая мне отличить Foo<1> от Foo<'1'>? По сути, я хочу установить флаг времени компиляции (в перечислении), чтобы указать, был ли передан int или char, без явного указания их.

EDIT Ответы заставили меня понять, что мой вопрос подразумевает, что мне действительно нужны (во время компиляции) экземпляры этих объектов. Я не...

Предположим, что каким-то образом стандарт позволил бы мне перегрузить шаблон класса таким образом, чтобы Foo<1> и Foo<'1'> были разными типами и содержали разные значения для своего поля flag. Затем эти типы сами могут быть переданы в другой шаблон класса, который может проверять их и делать с ними интересные вещи, например:

template <typename FooType>
struct Bar
{
    typedef typename If < FooType::flag, int, char >::Type Type;
};

Это все очень легко сделать, когда вы ничего не имеете против передачи типа явно, но это кажется излишним...


person JorenHeit    schedule 10.01.2014    source источник
comment
Хм, я никогда не думал об этом. +1   -  person Lightness Races in Orbit    schedule 10.01.2014
comment
Я тоже, пока мне это не понадобилось ;-)   -  person JorenHeit    schedule 10.01.2014
comment
Это похоже на более общий случай моего вопроса, ответ на который, боюсь, не обнадеживает.   -  person Useless    schedule 10.01.2014
comment
@Useless Ты прав, не очень обнадеживает.   -  person JorenHeit    schedule 10.01.2014
comment
Будут ли работать два шаблона, один из которых определен как template<int Val>, а другой — как template<char Val>, или компилятор будет бормотать о нескольких определениях?   -  person Sebastian Hoffmann    schedule 10.01.2014
comment
@Paranaix: Извините, я сошел с ума, предыдущий комментарий удален. GCC говорит что-то о повторном объявлении параметров шаблона.   -  person JorenHeit    schedule 10.01.2014
comment
Я не знаю, работает ли это или вообще компилируется, потому что VS еще не полностью поддерживает constexpr, но вы можете хотя бы попробовать с GCC, и, возможно, этот подход уже вам поможет: pastebin.com/Y04gi3hu Идея состоит в том, чтобы использовать вывод типов (что нам действительно нужно), но с функциями constexpr.   -  person Sebastian Hoffmann    schedule 10.01.2014
comment
@Paranaix Вы забыли указать конечный тип возврата в своем коде.   -  person Constructor    schedule 11.01.2014


Ответы (3)


Вы можете использовать макрос:

#define MAKE_FOO(value) \
    Foo<decltype(value), value>()

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

person Constructor    schedule 10.01.2014
comment
Я боялся, что это единственный ответ. Конечно, я уже рассматривал этот вариант, но макросы - это зло ;-) Отмечу как ответ, если ни у кого нет идеи получше. - person JorenHeit; 10.01.2014
comment
Как насчет того же подхода, но с параметрами шаблона по умолчанию? template<typename T = decltype(Val), T Val> Но почему-то я уверен, что это не скомпилируется :D - person Sebastian Hoffmann; 10.01.2014
comment
@Paranaix это не сработает, потому что параметры по умолчанию нужно заполнять справа. - person mpark; 10.01.2014
comment
В итоге я написал макрос, чтобы скрыть уродливые вещи от конечного пользователя, хотя это очень плохой стиль... Помечено как ответ! - person JorenHeit; 11.01.2014

Если вам просто нужны значения, доступные во время компиляции, но на самом деле они не нужны как часть типа (например, если вам не нужны Foo<int, 1> и Foo<int, 2> для разных типов), вы можете использовать конструктор constexpr вместе с функцией constexpr для создания Foo экземпляров во время компиляции.

#define DECLTYPE_AUTO(expr) \
  -> decltype(expr) { return expr; }

template <typename T>
struct Foo {

  constexpr Foo(T t) : value(t) {}

  T value;

};  // Foo

template <typename T>
constexpr auto MakeFoo(T val)
    DECLTYPE_AUTO(Foo<T>(val));

static_assert(MakeFoo(2).value == 2, "");
static_assert(MakeFoo('1').value == '1', "");

int main() {}
person mpark    schedule 10.01.2014
comment
Интересно... Однако я думаю, что это не очень полезно для меня. Моя вина полностью в том, что я не указал достаточно подробностей в вопросе. Вопрос отредактирован :-) - person JorenHeit; 10.01.2014

Вы можете сделать что-то вроде этого:

template <int Val>
struct Foo<int, Val> 
{
    static MyEnum tag = myTag1;
};


template <char Val>
struct Foo<char, Val>
{
    static MyEnum tag = myTag2;
};
person Kit Fisto    schedule 10.01.2014
comment
А цель какая? - person Sebastian Hoffmann; 10.01.2014
comment
Отличить Foo‹1› от Foo‹'1'›? - person Kit Fisto; 10.01.2014
comment
Вам все еще нужно передать либо int, либо char в Foo, чего я и хочу избежать. Я хочу, чтобы компилятор определял тип, как это происходит при перегрузке функций. сообщение от Useless касается точно такой же проблемы, и затем был сделан вывод, что это невозможно... - person JorenHeit; 10.01.2014
comment
Конечно, это невозможно. Поэтому я подумал, что описанный выше подход может помочь вам с вашей идеей и упростить вашу жизнь. - person Kit Fisto; 10.01.2014
comment
Это именно то, что я уже сделал в своем вступительном посте. Если бы это меня устраивало, я бы вообще не спрашивал. - person JorenHeit; 10.01.2014
comment
Я неправильно понял ваш вопрос. Ваша проблема заключается в создании экземпляров, которые впоследствии не различаются. Извиняюсь. - person Kit Fisto; 10.01.2014
comment
@KitFisto Ага! Без проблем :-) - person JorenHeit; 10.01.2014