2012-04-17 4 views
5

Я пытаюсь объединить специализации, чтобы не писать их несколько раз. Например, в приведенном ниже коде я стараюсь специализировать «float» и «double» как один случай реализации для foo :: func(); Затем я использую другую реализацию для «bool».Почему этот зависимый тип не считается специализацией с использованием аргумента шаблона?

template<typename T> struct foo; 

template<typename T> struct bar; 
template<> struct bar<float> { typedef float Type; }; 
template<> struct bar<double> { typedef double Type; }; 

/* specialize for float and double here */ 
template<typename T> struct foo<typename bar<T>::Type> { 
    static void func() { ... } 
}; 

template<> struct foo<bool> { 
    static void func() { ... } 
}; 

Данные ошибки в GCC 4.4.3. (Это целевой компилятор, потому что запас для Ubuntu Server 10.04 LTS, который якобы имеет более трех лет жизни.) Ошибка:

foo.cpp:8: error: template parameters not used in partial specialization: 
foo.cpp:8: error:   ‘T’ 

ошибка относится к первой специализации обув (для " float "и" double. ")

Я не вижу, какую часть C++ я здесь нарушаю - если кто знает эту главу и стих, я был бы признателен. Кроме того, если кто-то знает о другом способе достижения той же цели (повторное использование специализаций для определенных групп типов, без излишне подробного кода), я также был бы признателен за любые предложения!

ответ

6
template<typename T> struct foo<typename bar<T>::Type> { 
    static void func() { ... } 
}; 

Вы используете T в не выводимом контексте, поэтому компилятор не может вывести T, даже если он знает значение bar<T>::Type.

Предположим, что вы пишете,

foo<double> foodouble; 

, то вы, вероятно, думаете, bar, которая специализируется с double будет выбран при инстанцировании foo? Это кажется разумным, только если компилятор может убедиться, что не существует другой специализации bar, которая определяет double, как вложенного типа, что-то вроде этого:

template<> struct bar<int> { typedef double Type; }; 

Теперь bar<double>::Type и bar<int>::Type оба дают double. Таким образом, нижняя линия: может существовать бесконечное число специализации bar, все из которых могут содержать double как вложенный тип, что делает невозможным компилятор уникально вывести аргумент шаблона для шаблона класса bar.


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

#include <iostream> 

template<typename T> struct bar { typedef void type; }; 
template<> struct bar<float> { typedef bar<float> type; }; 
template<> struct bar<double> { typedef bar<double> type; }; 

template<typename T> 
struct foo : bar<T>::type 
{ 
    static void func() 
    { 
     std::cout << "primary template for float and double" << std::endl; 
    } 
}; 

template<> 
struct foo<bool> 
{ 
    static void func() 
    { 
     std::cout << "specialization for for bool" << std::endl; 
    } 
}; 

int main() 
{ 
    foo<float>::func(); 
    foo<double>::func(); 
    foo<bool>::func(); 
} 

выход (online demo):

primary template for float and double 
primary template for float and double 
specialization for for bool 

Обратите внимание, что struct foo : bar<T>::type не конкретизацией больше. Это первичный шаблон. Также обратите внимание, что это может быть не то, что вы хотите, поскольку оно отключает все экземпляры шаблона класса с аргументом типа, отличным от float, double и bool; например, вы не можете использовать foo<int>. Но затем я также отмечаю, что вы оставили основной шаблон неопределенным, поэтому я надеюсь, что это решение соответствует вашему требованию.

+0

Я был бы счастлив получить ошибку «неоднозначной спецификации шаблона», если есть многострочные бары с одинаковым объявлением типа. Компилятор действительно имеет всю необходимую ему информацию. Любопытно, что друг сообщает, что эта декларация РАБОТАЕТ на GCC 4.1 на MacOS X. В любом случае - спасибо за ответ и предложенную работу. Похоже, это может сработать для меня! –

2

Я не думаю, что то, что вы пытаетесь, возможно. Я нашел аналогичный вопрос here. У меня нет ссылки, но соответствующий раздел стандарта C++ - 14.8.2.5. Вот цитата из раздела о выводе аргументов шаблона:

The non-deduced contexts are: 
— The nested-name-specifier of a type that was specified using a qualified-id. 
— A non-type template argument or an array bound in which a subexpression references a template 
parameter. 
— A template parameter used in the parameter type of a function parameter that has a default argument 
that is being used in the call for which argument deduction is being done. 
— A function parameter for which argument deduction cannot be done because the associated function 
argument is a function, or a set of overloaded functions (13.4), and one or more of the following apply: 
— more than one function matches the function parameter type (resulting in an ambiguous deduc- 
tion), or 
— no function matches the function parameter type, or 
— the set of functions supplied as an argument contains one or more function templates. 
— A function parameter for which the associated argument is an initializer list (8.5.4) but the parameter 
does not have std::initializer_list or reference to possibly cv-qualified std::initializer_list 
type. [ Example: 
template<class T> void g(T); 
g({1,2,3}); 
// error: no argument deduced for T 
— end example ] 
— A function parameter pack that does not occur at the end of the parameter-declaration-clause. 

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

Не давая ей слишком много думали, как быстрый обходной путь вы можете добавить второй параметр Его не типа для Foo - что-то вроде:

template<typename T, bool is_decimal = false> 
struct foo {...}; // general case 

template<typename T> 
struct foo<T, true> { ... }; // case where T is float or double 

удачи ...

+0

Баллы для справки! Печаль о том, что T явно не выведена, хотя (в данном случае) это можно было бы вывести, а двусмысленность или не обнаружено надлежащим образом. –