Aktualisieren: bedingt explizit hat es in den C++20 Entwurf geschafft. mehr auf cpreference
Das cpreference std::tuple Konstruktorseite hat eine Reihe von C++ 17-Notizen, die Dinge sagen wie:
Dieser Konstruktor ist explicit
dann und nur dann, wenn std::is_convertible<const Ti&, Ti>::value
ist für mindestens einen falsch i
Wie kann man einen bedingt expliziten Konstruktor schreiben? Die erste Möglichkeit, die mir in den Sinn kam, war explicit(true)
aber das ist keine legale Syntax.
Ein Versuch mit enable_if
war nicht erfolgreich:
// constructor is explicit if T is not integral
struct S {
template <typename T,
typename = typename std::enable_if<std::is_integral<T>::value>::type>
S(T) {}
template <typename T,
typename = typename std::enable_if<!std::is_integral<T>::value>::type>
explicit S(T) {}
};
mit dem fehler:
error: ‘template<class T, class> S::S(T)’ cannot be overloaded
explicit S(T t) {}
Der Vorschlag, der das hinzufügte N4387: Paar und Tupel verbessern, Revision 3 hat ein Beispiel, wie es funktioniert:
Betrachten Sie die folgende Klassenvorlage A, die als Wrapper für einen anderen Typ T verwendet werden soll:
#include <type_traits>
#include <utility>
template<class T>
struct A {
template<class U,
typename std::enable_if<
std::is_constructible<T, U>::value &&
std::is_convertible<U, T>::value
, bool>::type = false
>
A(U&& u) : t(std::forward<U>(u)) {}
template<class U,
typename std::enable_if<
std::is_constructible<T, U>::value &&
!std::is_convertible<U, T>::value
, bool>::type = false
>
explicit A(U&& u) : t(std::forward<U>(u)) {}
T t;
};
Die gezeigten Konstruktoren verwenden beide perfekte Weiterleitung und sie haben im Wesentlichen die gleichen Signaturen, außer dass eine explizit ist, die andere nicht. Darüber hinaus sind sie gegenseitig ausschließlich eingeschränkt. Mit anderen Worten: Diese Kombination verhält sich für jeden Zieltyp T und jeden Argumenttyp U wie ein einzelner Konstruktor, der entweder explizit oder nicht explizit (oder überhaupt kein Konstruktor) ist.
Wie Praetorian betont, ist dies genau so libstdc++ implementiert es.
Wenn wir das OPs-Beispiel entsprechend modifizieren, funktioniert es auch:
struct S {
template <typename T,
typename std::enable_if< std::is_integral<T>::value, bool>::type = false>
S(T) {}
template <typename T,
typename std::enable_if<!std::is_integral<T>::value, bool>::type = false>
explicit S(T) {}
};
Eine Möglichkeit, die mit den meisten Compilern zu funktionieren scheint, besteht darin, einer der Funktionen einen Dummy-Parameter hinzuzufügen, um sie etwas anders zu machen.
// constructor is explicit if T is integral
struct S {
template <typename T,
typename = typename std::enable_if<std::is_integral<T>::value>::type>
S(T t) {}
template <typename T,
typename = typename std::enable_if<!std::is_integral<T>::value>::type,
typename dummy = void>
explicit S(T t) {}
};
int main()
{
S s1(7);
S s2("Hello");
}
Kompiliert mit MSVC 2015.
Das Vorschlag hinzugefügt dass der Normentwurf einige Beispiele enthält.
– Shafik Yaghmour
7. Oktober 2015 um 17:37 Uhr
In Ihrem Beispiel sind beide Konstruktoren gleich, da Standardargumente nicht von der Signatur getrennt sind. Vielleicht so etwas wie coliru.stacked-crooked.com/a/6db9921c59138c60
– David G
7. Oktober 2015 um 17:37 Uhr
Mein Gott, es ist einfach… schrecklich! Kein C++17 für mich, das ist erledigt.
– Leichtigkeitsrennen im Orbit
7. Oktober 2015 um 17:39 Uhr
Siehe libstdc++ Implementierung, es ist genau das, was Shafik gepostet hat. @LightnessRacesinOrbit IMO, das ist eine nette Lösung, weil Sie damit schreiben können
return {x ,y, z};
in einer Funktion, die a zurückgibttuple<Tx, Ty, Tz>
vorausgesetzt, alle beteiligten Typen sind implizit konvertierbar.– Prätorianer
7. Oktober 2015 um 17:59 Uhr
@David: Menschen, die über X nachdenken wollen, erfolgreich davon zu überzeugen, dass sie es nicht tun sollten, bis Y geklärt ist, bedeutet oft nur, dass weder X noch Y passieren. 😛
– Benutzer1084944
8. Oktober 2015 um 0:56 Uhr