Implizite Initialisierung funktioniert nicht mit Template-Konstruktor



  • Hi, wie im (Pseudo-)Code unten zu sehen ist gibt es eine Klasse "CString" bei der die implizite Initialisierung über den TSpan<char> Konstruktor und einem char[] Array funktioniert. Bei der TString Klasse mit generischem TSpan<T> Konstruktor dagegen nicht. Warum?

    template <class T>
    class TSpan
    {
    public:
    	TSpan() : _length(0) { };
    	TSpan(const TSpan<T>& span) : _length(span._length) { };
    
    	template <size_t N>
    	TSpan(const T (&value)[N]) :
    		_length(N)
    	{
    		printf("Size: %lu\n", N);
    	}
    
    	template<size_t N>
    	TSpan<T>& operator=(const T (&value)[N])
    	{
    		return *this;
    	}
    
    private:
    	int _length;
    
    };
    
    class CString
    {
    public:
    	CString() { };
    	CString(const CString& str) { };
    
    	CString(const TSpan<char>& span) { };
    
    };
    
    class TString
    {
    public:
    	TString() { };
    	TString(const TString& str) { };
    
    	template <class T>
    	TString(const TSpan<T>& span) { };
    
    };
    
    int main()
    {
    	TSpan span("Hello World!");
    	CString str1(span);
    	TString str2(span);
    	//TString str3("Hello World!"); // Geht nicht!
    	return 0;
    }
    

    Fehlermeldung:
    error: no matching constructor for initialization of 'TString'
    note: candidate template ignored: could not match 'TSpan<type-parameter-0-0>' against 'char const[13]'



  • Ich bin kein Template Profi. Aber meines Wissens nach gibt es keine Möglichkeit, das Template-Argument für einen Konstruktor zu spezifizieren.

    Mach es doch einfach so:

    template< typename T >
    class TString
    {
    public:
        TString(){};
        TString( const T& str ){};
        
    };
    
    int main()
    {
        TSpan<char> span( "Hello World!" );
        CString str1( span );
        TString<TSpan<char>> str2( span );
        TString<TSpan<char>> str3("Hello World!");
    }
    


  • TString kann leider kein Template sein. Doch C++17 ermöglicht template argument deduction auch für Kosntruktoren wenn ich das richtig verstanden habe. Aber das wurde ganz offensichtlich wie so vieles die letzten Jahre nur halbherzig umgesetzt. Evtl. ließe sich das über einen "deduction guide" (Gott ist das häßlich) lösen?

    https://en.cppreference.com/w/cpp/language/class_template_argument_deduction



  • Mach das mal:

    class TString
    {
    public:
        
        template< typename T, enable_if_t< is_convertible_v<T,TSpan<char>>, int > = 0 >
        TString( const T& span )
        {
        }
    };
    
    int main()
    {
        TSpan span{ "Hello World!" };
        CString str1{ span };
        TString str2{ span };
        TString str3{ "Hello World!" }; // Geht!
        TString str4{ str3 };
    }
    

  • |  Mod

    Wird ein Funktionsargument zur Deduktion eines Templateparameters herangezogen, können nicht gleichzeitig implizite Konvertierungen für dieses Funktionsargument durchgeführt werden. Auf die Problematik, das richtige T für TSpan zu finden (hierfür könnte ein deduction guide herangezogen werden), kommt es gar nicht erst an.

    Eine Möglichkeit wäre, die Konvertierung zu verzögern:

    template <typename T, size_t N>
    TSpan(const T(&)[N]) -> TSpan<T>;
    
    class TString
    {
    public:
    	TString() { };
    	TString(const TString& str) { };
    
    	template <class T>
    	TString(const TSpan<T>& span) { };
    
    	template <class T>
    	TString(const T& x) : TString(TSpan(x)) { };
    };