2013-04-09 5 views
36

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

class ReturnTypeSpecialization 
{ 
public: 
    template<typename T> 
    T Item(); 
}; 

// Normally just return the template type 
template<typename T> 
T ReturnTypeSpecialization::Item() { ... } 

// When a float is specified, return an int 
// This doesn't work: 
template<float> 
int ReturnTypeSpecialization::Item() { ... } 

Возможно ли это? Я не могу использовать C++ 11.

+0

что вы пытаетесь достичь? – didierc

+0

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

+0

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

ответ

38

Поскольку специализация должна согласиться с базовым шаблоном на тип возвращаемого значения, вы можете сделать так, добавив «тип признака возврата», на структуру вы можете специализироваться и сделать истинный тип возвращаемого из:

// in the normal case, just the identity 
template<class T> 
struct item_return{ typedef T type; }; 

template<class T> 
typename item_return<T>::type item(); 

template<> 
struct item_return<float>{ typedef int type; }; 
template<> 
int item<float>(); 

Live example.

Обратите внимание, что вы можете придерживаться следующего, так что вам нужно только обновить возвратный тип в item_return специализации.

template<> 
item_return<float>::type foo<float>(){ ... } 
// note: No `typename` needed, because `float` is not a dependent type 
+1

+1 Мне нравится этот: P – Rapptz

+2

Убедитесь, что все специализации объявлены до того, как они могут быть созданы. Практически это означает, что специализация ДОЛЖНА быть объявлена ​​в том же заголовочном файле, и декларации должны быть как можно ближе друг к другу. – aschepler

+0

@aschepler: Очень хорошая точка. Сквозная специализация в этой форме довольно волатильна. – Xeo

4

Вы можете сделать специализации шаблонов так:

template<typename T> 
T item() { 
    return T(); 
} 

template<> 
float item<float>() { 
    return 1.0f; 
} 
+2

Это означало бы, что 'T' также является всегда возвращаемым типом, который из примера в вопросе не выглядит так. – Xeo

+0

@Zeta: Вы можете полностью их специализировать (также называемую * явной специализацией *). – Xeo

+0

@Xeo: Название вопроса: * " Переопределение возвращаемого типа в функции шаблона специализации " *. Вероятно, опечатка в содержании вопроса. – Zeta

4

Может быть, вы могли бы использовать следующий хак. Учитывая эти простые черты типа:

template<bool b, typename T, typename U> 
struct conditional { typedef T type; }; 

template<typename T, typename U> 
struct conditional<false, T, U> { typedef U type; }; 

template<typename T, typename U> 
struct is_same { static const bool value = false; }; 

template<typename T> 
struct is_same<T, T> { static const bool value = true; }; 

Вы можете написать свой класс и специальную функцию-член следующим образом:

class ReturnTypeSpecialization 
{ 
public: 
    template<typename T> 
    typename conditional<is_same<T, float>::value, int, T>::type 
    Item(); 
}; 

// Normally just return the template type 
template<typename T> 
typename conditional<is_same<T, float>::value, int, T>::type 
ReturnTypeSpecialization::Item() { return T(); } 

// When a float is specified, return an int 
template<> 
int ReturnTypeSpecialization::Item<float>() { return 1.0f; } 

Простая тестовая программа (использует C++ 11 только для проверки):

int main() 
{ 
    ReturnTypeSpecialization obj; 
    static_assert(std::is_same<decltype(obj.Item<bool>()), bool>::value, "!"); 
    static_assert(std::is_same<decltype(obj.Item<float>()), int>::value, "!"); 
} 

Адрес live example.

+0

У него нет C++ 11. – Rapptz

+0

@ Rapptz: Хорошо, извините, я пропустил это. Но я считаю, что это можно было бы переписать без использования какой-либо возможности C++ 11 ('std :: conditional' не сложно переписать) –

+0

Я думаю, что у вас есть ваше состояние неправильно. Кроме того, это довольно сложно, чтобы распространиться на другие специализации, и, что еще хуже, все специализации должны быть известны заранее. – Xeo

2

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

#include <iostream> 
using std::cout; 

// worker class -- return a reference to the given value 
template< typename V > struct worker 
    { 
    typedef V const & type; 
    static type get(V const & v) { return v; } 
    }; 

// worker class specialization -- convert 'unsigned char' to 'int' 
template<> struct worker<unsigned char> 
    { 
    typedef int type; 
    static type get(unsigned char const & v) { return v; } 
    }; 

// mapper function 
template< typename V > typename worker<V>::type mapper(V const & v) 
    { 
    return worker<V>::get(v); 
    } 

int main() 
    { 
    char a='A'; 
    unsigned char b='B'; 
    cout << "a=" << mapper(a) << ", b=" << mapper(b) << "\n"; 
    } 

В этом примере, специализация unsigned char заставляет его быть преобразованы в int таким образом, что cout отобразит его в виде числа, а не в качестве символа, генерируя следующий вывод ...

a=A, b=66 
+0

Это имеет ту же основную механику, что и ответ @ Xeo, но может считаться немного более чистым, поскольку требуется только одна специализация. Другая функция, как реализовано, состоит в том, что нормальный случай по существу является NOP - он просто возвращает ссылку на исходное значение. – nobar

+0

Мне тоже нравится это решение. Если вы хотите специально запретить общее решение и разрешить только явные шаблоны, первая структура может быть изменена только на 'template struct worker;', no body. – Eyal

Смежные вопросы