2016-09-20 2 views
1

Почему компилятор может вывести T с этим кодом:C++ параметр шаблона вычет не может

#include <vector> 

template<typename T> 
void foo(T& t) {} 

int main(void) { 
    std::vector<uint8_t> vec = { 1,2,3 }; 
    foo(vec); 
    return 0; 
} 

Но терпит неудачу с этим кодом:

#include <vector> 
#include <type_traits> 

template<typename T> 
void foo(typename std::enable_if<true, T>::type& t) {} 

int main(void) { 
    std::vector<uint8_t> vec = { 1,2,3 }; 
    foo(vec); 
    return 0; 
} 

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

+2

Может быть специализация 'std :: enable_if ', которая содержит 'using type = std :: vector '. Как компилятор должен знать, когда он имеет только тип параметра? –

ответ

4

Как пояснил vsoftco, у вас есть не-вывод, контекст.

Для SFINAE, вы можете использовать один из следующих вариантов:

template<typename T> 
std::enable_if_t<condition_dependent_of_T, ReturnType> 
foo(T& t) {} 

или

template<typename T, std::enable_if_t<condition_dependent_of_T>* = nullptr> 
ReturnType foo(T& t) {} 
+0

Он отлично работает. Но я не могу понять, почему condition_dependent_of_T действительно должен зависеть от T. Когда я помещаю туда оцененные константы, код не компилируется, почему? – omicronns

+2

@omicronns 'T' выводится из аргумента' T & t', а не из 'enable_if'. Невыводимые контексты не выводятся, поскольку для компилятора чрезвычайно сложно (и когда-то невозможно) выводить из них тип. 'Enable_if' ** должен ** зависеть от' T', поскольку 'T' будет выведено, тогда SFINAE будет входить. – vsoftco

+0

@ Jarod42 Мне жаль, что можно объединить ответы на SO :) – vsoftco

6

Во втором случае у вас есть non-deduced context, другими словами, компилятор не может вывести тип.

Простейший пример не-выводится контексте

template<typename T> 
struct Id 
{ 
    using type = T; 
}; 

template<typename T> 
void foo(typename Id<T>::type arg); // T cannot be deduced 
+2

Для решения проблемы необходимо разделить контекст для вычета и enable_if. Обычная практика заключается в использовании enable_if в возвращаемом значении функции, например: 'template typename std :: enable_if > :: значение, void> :: type foo (T & t) {} ' –

2

Чтобы визуализировать проблему, давайте проанализируем пример:

template <class> 
struct foo { 
    using type = float; 
}; 

template <> 
struct foo<bool> { 
    using type = int; 
}; 

template <> 
struct foo<int> { 
    using type = int; 
}; 

template <class T> 
void bar(foo<T>::type t) { } 

int main() { 
    bar(int{}); 
} 

Теперь в строке bar(int{}); оба типа bool, а также int соответствует температуре поздний параметр T. Какое значение должно быть выведено тогда? Это только один пример, почему неустановленный контекст строго необходим!

+1

рекомендуется для очень приятного примера! – vsoftco

+0

Хорошо. Но компиляторы знают эти неоднозначные специализации посредством барного вызова. Когда foo нет (или foo ), то нет никакой двусмысленности. – omicronns

+1

@omicronns Да, но тогда представьте, сколько правил и исключений необходимо добавить к уже супер сложному набору правил. И иногда компилятор может не знать: что, если ваши специализации определены в разных единицах компиляции? Компилятор не делает никаких предположений о других устройствах. – vsoftco