Konstruktor bedingt als explizit gekennzeichnet

Lesezeit: 5 Minuten

Benutzer-Avatar
Ryan Haining

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) {}

  • 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ückgibt tuple<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


Benutzer-Avatar
Shafik Yaghmur

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) {}
};

  • Beachten Sie, dass bei strenger Lektüre des Standards der obige Code für einige Typen schlecht definiert sein könnte T. Solcher Code in std ist kein Problem, denn die Umsetzung von std ist ein schwarze Box (es steht ihnen frei, UB zu machen oder ein technisch schlecht geformtes Programm zu haben, solange der Compiler das Richtige als Reaktion auf besagtes UB/schlecht geformte tut). Die Frage ist, muss eine Vorlagenmethode einer Vorlagenklasse mindestens eine gültige Instanziierung für jeden Satz gültiger Vorlagenklassenparameter haben? Der Standard ist unklar, als ich zuletzt nachgesehen habe.

    – Yakk – Adam Nevraumont

    7. Oktober 2015 um 21:26 Uhr


  • @Yakk Ich werde für eine Weile keine wirklichen Änderungen vornehmen können, aber ich glaube, Sie haben Recht. Ich hatte die Tinte, aber ich ließ den Gedanken entgleiten.

    – Shafik Yaghmour

    7. Oktober 2015 um 22:20 Uhr

  • @Yakk Ich kann anscheinend kein Zitat finden, das genau auf diesen Fall zutrifft. Wenn ich richtig lese, denke ich, dass die Anforderung eine gültige Instanziierung ist, nicht unbedingt für jeden Parametersatz. Haben Sie Mängelberichte etc…?

    – Shafik Yaghmour

    8. Oktober 2015 um 3:23 Uhr


  • “Wenn für eine Vorlage keine gültige Spezialisierung generiert werden kann und diese Vorlage nicht instanziiert wird, ist die Vorlage falsch formatiert, keine Diagnose erforderlich.” [temp.res]/8 in n4527. Als Vermutung für den Grund scheint es ein Allheilmittel für “Compiler sind frei, Vorlagencode zu erkennen, der für keinen Typ gültig sein könnte” (was auch die Möglichkeit eröffnet, dem Standard weitere Gültigkeitsprüfungen hinzuzufügen). Die Frage ist, gilt dies für Template-Methoden von Template-Klasseninstanzen oder nicht?

    – Yakk – Adam Nevraumont

    8. Oktober 2015 um 3:45 Uhr

  • @Yakk Ich habe das gesehen, aber es ist nicht klar, ob es auf diesen Fall zutrifft oder nicht, ich werde heute ein wenig mehr darauf eingehen.

    – Shafik Yaghmour

    8. Oktober 2015 um 9:26 Uhr

Benutzer-Avatar
Bo Persson

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.

  • typename std::enable_if<test, int>* =0 funktioniert ohne die dummy.

    – Yakk – Adam Nevraumont

    7. Oktober 2015 um 21:29 Uhr

1012110cookie-checkKonstruktor bedingt als explizit gekennzeichnet

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy