2016-09-27 4 views
1

В этом вопросе действительно нет фона.SFINAE для пользовательского преобразования

Существует много способов использования SFINAE (прямо или косвенно с type_traits) для проверки существующей функции, функции-члена и т. Д. Но:

Первый вопрос: Есть ли способ проверить, реализует ли класс определенный пользовательский оператор преобразования?

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

#include <type_traits> 
#include <cassert> 

struct NotADouble { 
}; 
struct Double { 
    // explicit or not - you decide 
    explicit operator double() { 
     return 1.; 
    } 
}; 
// CORRECT THIS: ... 
template<typename T, 
    class = decltype(static_cast<double>(std::declval<T>()))> 
int f(T) { 
    return 1; 
} 
int f(...) { 
    return 2; 
} 
// ... UNTIL HERE 

int main() { 
    assert(f(NotADouble()) == 2); 
    assert(f(Double()) == 1); 
    assert(f(3.) == 2); 
    assert(f(3) == 2); 
    assert(f(3.f) == 2); 
} 

Текущая реализация f проверяет, есть ли стандартная последовательность преобразования из T в double, я предполагаю, что это то же самое, как std::is_convertible в этом случае.

Другим подходом будет следующая реализация, которая принимает первые два теста.

template<typename T> 
int f(T, double (T::*)() = nullptr) { 
    return 1; 
} 
int f(...) { 
    return 2; 
} 

Вопрос 2: Несмотря на то, NotADouble не реализует какой-либо оператор преобразования, по-видимому, чтобы эту функцию указателя. Итак, что же такое double (T::*)() и почему оно существует для любого класса?

ответ

0

Первый вопрос: вы можете создать вспомогательный-структуру, чтобы проверить, если определен оператор преобразования в данной структуре (C++ 14 код):

#include <type_traits> 
#include <iostream> 

struct NotDouble { }; 
struct Double { 
    explicit operator double() { 
     return 1.0; 
    } 
}; 

template <class From, class To, class = void> 
struct HasConversionOperator: std::false_type { }; 

template <class From, class To> 
struct HasConversionOperator<From, To, decltype((&From::operator To), void())>: std::true_type { }; 

template <class From, class To, class = void> 
struct HasExplicitConversion: std::false_type {}; 

template <class From, class To> 
struct HasExplicitConversion<From, To, decltype(std::declval<To&>() = (To)std::declval<From&>(), void())>: std::true_type { }; 

template <class From, class To, class = void> 
struct HasImplicitConversion: std::false_type {}; 

template <class From, class To> 
struct HasImplicitConversion<From, To, decltype(std::declval<To&>() = std::declval<From&>(), void())>: std::true_type { }; 

template <class T> 
std::enable_if_t<HasConversionOperator<T, double>::value && HasExplicitConversion<T, double>::value && !HasImplicitConversion<T, double>::value> is_double(T d) { 
    std::cout << "has conversion to double" << std::endl; 
} 

template <class T> 
std::enable_if_t<!HasConversionOperator<T, double>::value || !HasExplicitConversion<T, double>::value || HasImplicitConversion<T, double>::value> is_double(T) { 
    std::cout << "don't have conversion to double" << std::endl; 
} 

int main() { 
    is_double(Double{}); 
    is_double(NotDouble{}); 
} 

Выход:

has conversion to double 
don't have conversion to double 

Второй вопрос:double (T::*)() является типом любого члена указатель функции с владельцем T, который возвращает double, который не принимает никаких параметров. Имейте в виду, что такие подписчики имеют не только операторы преобразования. Более того, даже если класс T не имеет какой-либо членной функции, которая возвращает double и не принимает никакого параметра, ему вполне разрешено создавать такой тип указателя, один просто не сможет заполнить переменную типа указателя любым значением.

+0

Был аналогичный тест, который я пробовал, но без ссылки, чего явно не хватало ... – overseas

+0

@ user3445587 Вы имеете в виду оператор 'address of' (&). Да, к сожалению, нет способа взять ссылку на функцию-член, что явно является недостатком, но оно напрямую связано с синтаксисом вызова метода C++. Например. в 'a.foo();' как 'foo' следует интерпретировать? В качестве функции-члена функции 'a' или ссылки функции-члена доступны в области ...? –

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