2014-12-11 5 views
6

Я пытаюсь перегрузить operator T() с помощью SFINAE, чтобы вернуть копию, когда T является фундаментальным типом, и константой ссылки, когда T является классом.Включить оператор преобразования, используя SFINAE

При использовании double в моем примере ниже, я не могу получить вторую перегрузку (с std::is_class), которую необходимо удалить.

То есть ошибка, я получаю:

error: no type named ‘type’ in ‘struct std::enable_if<false, const double&>’ 
operator typename std::enable_if< std::is_class<T>::value, const T&>::type() const 
^ 

Что я делаю неправильно?

#include <iostream> 
#include <type_traits> 

template<typename T> 
struct Foo 
{ 
    operator typename std::enable_if<!std::is_class<T>::value, T >::type() const 
    { 
     return _val; 
    } 

    operator typename std::enable_if< std::is_class<T>::value, const T&>::type() const 
    { 
     return _val; 
    } 

    T _val; 
}; 

int main() 
{ 
    Foo<double> f1; 
    f1._val = 0.3; 

    double d = f1; 
    std::cout << d << std::endl; 
    return 0; 
} 

ответ

9

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

template<typename U = T> 
operator typename std::enable_if<!std::is_class<U>::value, U >::type() const 
{ 
    return _val; 
} 

template<typename U = T> 
operator typename std::enable_if< std::is_class<U>::value, const U&>::type() const 
{ 
    return _val; 
} 

Live demo

+0

Для немного другого примера и дополнительной информации см. Http://stackoverflow.com/questions/18100297/how-can-i-use-stdenable-if-in-a-conversion-operator – Asher

+0

@Asher Использование SFINAE по умолчанию аргумент шаблона работает в вопросе, с которым вы связались, но в вопросе выше, где ОП пытается использовать его для определения двух взаимоисключающих перегрузок, он не будет [как описано здесь] (http://stackoverflow.com/а/29502338/241631). – Praetorian

+0

Возможно, это не позволяет компилятору создавать экземпляры нежелательных операторов преобразования типов? – Museful

-2

Вы должны прочитать определение станд :: enable_if

< шаблон BOOL B, класс T = пустота> структура enable_if;

«Если значение B истинно, std :: enable_if имеет открытый тип typedef, равный T, в противном случае нет члена typedef."

+2

'enable_if' предназначен для SFINAE (см [примеры] (http://en.cppreference.com/w/cpp/types/enable_if), поэтому здесь проблема не в том ЬурейеЕ здесь нет, но SFINAE работает не так, как ожидалось. – Johan

+0

@Johan, что значит «SFINAE не работает должным образом?» Он отлично выбирает второе определение оператора (из-за 'const T &' qualifier), параметры первого шаблона of enable_if в данный момент является 'false', вы не определяете' type', конец рассказа –

+2

См. это [ответ] (http://stackoverflow.com/a/27433342/2439734). – Johan

3

В то время как решение проблемы неправильного оператора не было отброшено, чтобы решить конкретную проблему под рукой, то есть для возврата по const ref для типов классов или по значению для других, можно найти решение используя std::conditional.

template< bool B, class T, class F > 
struct conditional; 

Обеспечивает члена типа ЬурейеЕ, который определяется как T, если В истинно в время компиляции, или как F, если В является ложным.

Рабочий пример:

#include <iostream> 
#include <type_traits> 

template<typename T> 
struct Foo 
{ 
    operator typename std::conditional< 
     std::is_class<T>::value, const T&, T>::type() const 
    { 
     return _val; 
    } 

    T _val; 
}; 

int main() 
{ 
    Foo<double> f1; 
    f1._val = 0.3; 

    double d = f1; 
    std::cout << d << std::endl; 
    return 0; 
} 
+1

. Это похоже на достойный способ избежать необходимости ответа на ваш вопрос, но стоит отметить, что он не может быть обобщен для работы, если тела вашего оператора не являются 100 % идентичны. – hvd

+0

@hvd Я согласен. Однако в этом конкретном случае, когда все, что я хочу сделать, это return by const ref для типов классов и по значению для других типов, я думаю, что использование 'std :: conditional' - это более простой/более короткий подход, чем наличие 2' std_enable_if' перегрузки. Согласитесь? –

+1

Да, если тела двух операторов идентичны, я бы определенно пошел с решением 'std :: conditional'. – Praetorian