2016-01-10 17 views
7

Mentre sperimentare con qualche vincolo modello costruisce, ho incontrato un comportamento sorprendente Clang 3.7:Nel caso in cui decltype su un parametro del valore del modello attiva un contesto SFINAE?

struct constraint_success {}; 
struct constraint_failure {}; 

template<bool> 
struct require_t {}; 

template<> 
struct require_t<true> { 
    static constexpr constraint_success* result = nullptr; 
}; 

template<> 
struct require_t<false> { 
    static constexpr constraint_failure* result = nullptr; 
}; 

template<bool Condition> 
constexpr auto require = require_t<Condition>::result; 

//A named dummy value, since we need a default 
constexpr constraint_success* required = nullptr; 

Questo decltype innesca un contesto SFINAE nel mio compilatore:

template<constraint_success* value> 
using constraint = decltype(value); 

Al contrario:

//template<constraint_success* value> 
//using constraint = constraint_success*; 

Esempio:

//define custom constraints 
template<typename T> 
constexpr auto Pointer = require<std::is_pointer<T>::value>; 

template<typename T> 
constexpr auto NotPointer = require<!std::is_pointer<T>::value>; 

//constrain template parameters 
template<typename T, constraint<Pointer<T>> = required> 
void foo() { 
    std::cout << "Hello, pointer!\n"; 
} 

template<typename T, constraint<NotPointer<T>> = required> 
void foo() { 
    std::cout << "Hello, not pointer!\n"; 
} 

int main() { 
    foo<int*>(); 
    foo<int>(); 
    return 0; 
} 

Ciò è richiesto dallo standard o è un bug "fortunato" del compilatore?

Wandbox link

risposta

8

modelli Alias ​​quali constraint vengono sostituiti all'interno di qualsiasi definizione di modello vengono usati in Sono speciale in questo modo:. Altri modelli vengono sostituiti una volta definiti gli argomenti vengono forniti.

Quindi, questa dichiarazione:

template<typename T, constraint<Pointer<T>> = required> 
void foo(); 

è approssimativamente equivalente al presente dichiarazione (la differenza essendo sostituzione di un static_cast per una conversione implicita):

template<typename T, decltype(static_cast<constraint_success*> 
             (require_t<std::is_pointer<T>>::result)) 
           = required> 
void foo(); 

Questo produce SFINAE quando il static_cast è non valido.

L'effetto è utile e in sostanza consente di emulare i vincoli dalla funzione Concetti imminenti. Tuttavia, questo particolare approccio è piuttosto complicato, e non lo stai davvero sfruttando.

L'approccio canonico SFINAE è questo:

template< typename T > 
std::enable_if_t< std::is_pointer<T>::value > foo(); 

Si può essere un po 'più pulito e intelligente, e rimuovere lo SFINAE dal tipo di ritorno - che cosa ti sembra di essere attualmente in corso per:

template< typename T, 
    std::enable_if_t< std::is_pointer<T>::value > * = nullptr > 
void foo(); 

Dati i modelli variabili nella biblioteca Fundamentals TS, ciò corrisponde simbolo-per-simbolo con il tuo esempio:

template< typename T, 
    std::enable_if_t< std::is_pointer_v<T> > * = nullptr > 
void foo(); 

// constraint  < Pointer   <T>>  = required> 

Il modo veramente Concepts-ish è così:

template< typename T, std::enable_if_t< std::is_pointer<T>::value > * = nullptr > 
using Pointer = T; 

template< typename T > 
void foo(Pointer<T>); 
Problemi correlati