2013-03-05 15 views
30

Sto lavorando all'aggiornamento di alcuni codici C++ per sfruttare la nuova funzionalità in C++ 11. Ho una classe dei caratteri con alcune funzioni che restituiscono tipi fondamentali che, nella maggior parte dei casi, potrebbero restituire un'espressione costante, ma non sempre. Mi piacerebbe fare cose diverse a seconda che la funzione sia constexpr o meno. Sono venuto con il seguente approccio:Rilevazione di constexpr con SFINAE

template<typename Trait> 
struct test 
{ 
    template<int Value = Trait::f()> 
    static std::true_type do_call(int){ return std::true_type(); } 

    static std::false_type do_call(...){ return std::false_type(); } 

    static bool call(){ return do_call(0); } 
}; 

struct trait 
{ 
    static int f(){ return 15; } 
}; 

struct ctrait 
{ 
    static constexpr int f(){ return 20; } 
}; 

int main() 
{ 
    std::cout << "regular: " << test<trait>::call() << std::endl; 
    std::cout << "constexpr: " << test<ctrait>::call() << std::endl; 
} 

Il parametro aggiuntivo int/... è là in modo che, se entrambe le funzioni sono disponibili dopo SFINAE, il primo viene scelto da sovraccarico risoluzione.

compilazione e l'esecuzione di questo con clangore 3,2 spettacoli:

regular: 0 
constexpr: 1 

Quindi questo sembra funzionare, ma mi piacerebbe sapere se il codice è legale C++ 11. Specialmente poiché è a mia conoscenza che le regole per SFINAE sono state modificate.

+0

problema interessante. Ho scritto una risposta che pensavo provasse che fosse legale, ma poi ho scritto una [versione modificata] (http://liveworkspace.org/code/SWmBI$5) che avrebbe dovuto essere ugualmente valida secondo la mia risposta, e quella non si compila su nessun compilatore. Quindi non ho intenzione di presentare la risposta, ma sono molto curioso. –

+3

Correlati: [È is_constexpr possibile in C++ 11?] (Http://stackoverflow.com/questions/13299394/is-is-constexpr-possible-in-c11). Inoltre, consultare [Chiamare constexpr nell'argomento modello predefinito] (http://stackoverflow.com/questions/10721130/calling-constexpr-in-default-template-argument). –

+0

@AndyProwl: Sono curioso, potresti pubblicare la tua soluzione su liveworkspace.org o su un altro sito simile? –

risposta

13

NOTA:I opened a question here sul fatto che il codice PO è in realtà valida. Il mio esempio riscritto di seguito funzionerà in ogni caso.


ma mi piacerebbe sapere se il codice è legale C++ 11

E ', anche se l'argomento modello predefinito può essere considerato un po' insolito. Personalmente, come il seguente stile migliore, che è simile a come si (leggi: I) scrivo un tratto a check for a function's existence, usando solo un parametro di template non-tipo e tralasciando il decltype:

#include <type_traits> 

namespace detail{ 
template<int> struct sfinae_true : std::true_type{}; 
template<class T> 
sfinae_true<(T::f(), 0)> check(int); 
template<class> 
std::false_type check(...); 
} // detail:: 

template<class T> 
struct has_constexpr_f : decltype(detail::check<T>(0)){}; 

Live example.


tempo Spiegazione ~

tuo codice originale funziona perché punto di istanziazione di un argomento template di default è il punto di instantiati sul suo modello di funzione, che significa, nel tuo caso, in main, quindi non può essere sostituito prima di quello.

§14.6.4.1 [temp.point] p2

Se un modello di funzione [...] si chiama in un modo che utilizza la definizione di un argomento di default di quel modello di funzione [...], il punto di istanza di default argomento è il punto di istanziazione del modello di funzione [...].

Dopodiché, sono solo le solite regole di SFINAE.


† Atleast Credo di sì, non è del tutto chiaro nella norma.

+0

Quella citazione dallo standard cancella tutti i miei dubbi, grazie. –

+0

+1 anche per la soluzione alternativa –

+0

sfortunatamente non penso sia così chiaro. un "argomento modello predefinito" non può essere specificato per le funzioni in C++ 03, tuttavia quel testo era presente anche lì. il testo parla solo degli argomenti predefiniti sulle funzioni e non sui modelli di classe. il che mi fa pensare che non parli di argomenti del template, ma di argomenti di funzione. –

1

Richiesto da @ marshall-clow, ho messo insieme una versione un po 'più generica di un tipo-tratto per rilevare constexpr. L'ho modellato su std::invoke_result, ma poiché constexpr dipende dagli input, gli argomenti del modello riguardano i valori passati, piuttosto che i tipi.

E 'un po' limitato, come i args modello può essere solo un limited set of types, e sono tutti const quando arrivano alla chiamata di metodo. È possibile testare facilmente un metodo wrapper constexpr se sono necessari altri tipi o valori non costanti per un parametro di riferimento.

Quindi un po 'più di esercizio e dimostrazione del codice effettivamente utile.

E l'utilizzo di template<auto F, auto... Args> rende C++ 17-only, che richiede gcc 7 o clang 4. MSVC 14.10.25017 non può compilarlo.

namespace constexpr_traits { 

namespace detail { 

// Call the provided method with the provided args. 
// This gives us a non-type template parameter for void-returning F. 
// This wouldn't be needed if "auto = F(Args...)" was a valid template 
// parameter for void-returning F. 
template<auto F, auto... Args> 
constexpr void* constexpr_caller() { 
    F(Args...); 
    return nullptr; 
} 

// Takes a parameter with elipsis conversion, so will never be selected 
// when another viable overload is present 
template<auto F, auto... Args> 
constexpr bool is_constexpr(...) { return false; } 

// Fails substitution if constexpr_caller<F, Args...>() can't be 
// called in constexpr context 
template<auto F, auto... Args, auto = constexpr_caller<F, Args...>()> 
constexpr bool is_constexpr(int) { return true; } 

} 

template<auto F, auto... Args> 
struct invoke_constexpr : std::bool_constant<detail::is_constexpr<F, Args...>(0)> {}; 

} 

Live demo with use-cases on wandbox