2016-11-15 20 views
7

W dokumencie n4502 autorzy opisują wczesną implementację idiomu wykrywania, który obejmuje sztuczkę void_t. Oto jego definicja wraz z używania do definiowania cechę dla is_assignable (naprawdę to is_copy_assignable)Użycie pustego szablonu w implementacji identyfikatora wczesnego wykrywania

template<class...> 
using void_t = void; 

// primary template handles all types not supporting the operation: 
template< class, template<class> class, class = void_t< > > 
struct 
detect : std::false_type { }; 
// specialization recognizes/validates only types supporting the archetype: 
template< class T, template<class> class Op > 
struct 
detect< T, Op, void_t<Op<T>> > : std::true_type { }; 

// archetypal expression for assignment operation: 
template< class T > 
using 
assign_t = decltype(std::declval<T&>() = std::declval<T const &>()); 

// trait corresponding to that archetype: 
template< class T > 
using 
is_assignable = detect<void, assign_t, T>; 

one wspomnieć, że nie podoba im się to ze względu na void stosowanego w is_assignable cechy:

Chociaż wynikowy kod był znacznie bardziej zrozumiały niż oryginał, nie znaliśmy powyższego interfejsu wykrywania, ponieważ argument metafunkcji jest szczegółem implementacji, który nie powinien przeciekać do kodu klienta.

Jednak void nie ma dla mnie żadnego sensu. Jeśli spróbuję użyć tej cechy do wykrycia, czy można przypisać do kopii int, otrzymam std::false_typeDemo.

Gdybym przepisać is_assignable jak:

template< class T > 
using 
is_assignable = detect<T, assign_t>; 

co sprawia, że ​​więcej sensu dla mnie, wtedy pojawia się cecha, aby działać poprawnie: Demo

Więc moje pytanie jest tu Am I nieporozumienia coś w tym dokument, czy był to po prostu literówka?

Jeśli był literówka, to ja nie rozumiem, dlaczego autorzy czuł potrzebę omówienia nie lubili void wycieka, co sprawia mi całkiem pewien, że jestem po prostu czegoś brakuje.

+2

Poprzednik N4502, [N4436] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4436.pdf), ma jeszcze bardziej skandaliczne literówki (niepoprawna liczba argumentów szablonu). Więc inny zestaw literówek nie jest wykluczony ...; -] – ildjarn

+4

Czy nie powinno to być 'using is_assignable = detect ' zamiast? – Rerito

+1

@Rerito Nie sądzę, że konieczne jest końcowe "void". Wierzę, że przyczyną tego zamieszania jest literówka, jak sugeruje Ildjarn. Jak wspomina autor w przypisach do papieru, być może jest to "cienki" –

Odpowiedz

1

Sądząc na jak napisali autorzy ostateczną realizację is_detected one przeznaczone że Op być o zmiennej liczbie argumentów szablonu, który pozwala, aby wyrazić wiele innych pojęć:

(ciągniętych Również z n4502)

// primary template handles all types not supporting the archetypal Op: 
template< class Default 
, class // always void; supplied externally 
, template<class...> class Op 
, class... Args 
> 
struct 
detector 
{ 
    using value_t = false_type; 
    using type = Default; 
}; 
// the specialization recognizes and handles only types supporting Op: 
template< class Default 
, template<class...> class Op 
, class... Args 
> 
struct 
detector<Default, void_t<Op<Args...>>, Op, Args...> 
{ 
    using value_t = true_type; 
    using type = Op<Args...>; 
}; 
//... 
template< template<class...> class Op, class... Args > 
using 
is_detected = typename detector<void, void, Op, Args...>::value_t; 

Po uzyskaniu takiego scenariusza konieczne staje się ustawienie void, aby specjalizacja szablonu była zgodna z wersją true_type, gdy Op<Args...> jest prawidłowym wyrażeniem.

Here's my tweak on the original detect to be variadic:

template<class...> 
using void_t = void; 

// primary template handles all types not supporting the operation: 
template< class, template<class...> class, class... > 
struct 
detect : std::false_type { }; 
// specialization recognizes/validates only types supporting the archetype: 
template< class T, template<class...> class Op, class... Args > 
struct 
detect< T, Op, void_t<Op<T, Args...>>, Args... > : std::true_type { }; 

A potem niech określić cechę przetestować dla funkcji Foo że może może lub nie może przyjąć pewne typy:

template<class T, class... Args> 
using HasFoo_t = decltype(std::declval<T>().Foo(std::declval<Args>()...)); 

template< class T, class... Args> 
using has_foo = detect<T, HasFoo_t, void, Args...>; 

I struct do testowania:

struct A 
{ 
    void Foo(int) 
    { 
     std::cout << "A::Foo(int)\n"; 
    } 
}; 

Na koniec test (y):

std::cout << std::boolalpha << has_foo<A, int>::value << std::endl; //true 
std::cout << std::boolalpha << has_foo<A>::value << std::endl; // false 

Jeśli usunąć void z mojego has_foo cechę, to podstawowy kierunek jest wybrany, a ja się false (Example).

Jest tak, ponieważ void istnieje, aby dopasować void_t<Op<T, Args...>>, który, jeśli pamiętasz, będzie miał typ void, jeśli argument szablonu jest poprawnie sformułowany. Jeśli argument szablonu nie jest dobrze sformułowany, wówczas void_t<Op<T, Args...>> nie jest dobrze dopasowany i powróci do domyślnej specjalizacji (false_type). Kiedy usuniemy void z naszego połączenia (i zostawimy po prostu Args...), nie będziemy mogli dopasować argumentu void_t<Op<T, Args...>> do specjalizacji .

Autorzy chcieli usunąć ten kod void, który znalazłby się w kodzie klienta, stąd ich nieco bardziej skomplikowana implementacja w dalszej części artykułu.

Dzięki @Rerito za wskazanie this answer gdzie Yakk wkłada trochę dodatkowej pracy, aby uniknąć brzydkiego void w kodzie klienta.

+2

Jeśli ten facet chce dostać coś zatwierdzonego w ten sposób , czy nie powinien on otrzymać proofa przeczytanego i przetestowanego? Bardzo denerwujące dla czytelników. – Adrian

Powiązane problemy