c++templatesctaddefault-template-argument

CTAD fails for templated base class


Consider the following simple construct. I derive a class type EntityView from Entity which allows me to specify an allocator if I want to, and if I don't it should fall back to the defaulted template parameter. Yet the compiler complains that it can't derive template arguments:

Demo

#include <iostream>
#include <stdexcept>
#include <string>
#include <memory>

template <typename StringType = std::string>
struct Entity {
    Entity(int) {
        std::cout << "Hello World!" << std::endl;
    }
};

template <typename Allocator = std::allocator<int>>
struct EntityView : public Entity<std::basic_string<char, std::char_traits<char>, Allocator>> {
    using Entity<std::basic_string<char, std::char_traits<char>, Allocator>>::Entity;
};

int main() {
    EntityView myview{2};
}

Yields (gcc):

<source>: In function 'int main()':
<source>:19:24: error: class template argument deduction failed:
   19 |     EntityView myview{2};
      |                        ^
<source>:19:24: error: no matching function for call to 'EntityView(int)'
<source>:14:8: note: candidate: 'template<class Allocator> EntityView()-> EntityView<Allocator>'
   14 | struct EntityView : public Entity<std::basic_string<char, std::char_traits<char>, Allocator>> {
      |        ^~~~~~~~~~
<source>:14:8: note:   template argument deduction/substitution failed:
<source>:19:24: note:   candidate expects 0 arguments, 1 provided
   19 |     EntityView myview{2};
      |                        ^
<source>:14:8: note: candidate: 'template<class Allocator> EntityView(EntityView<Allocator>)-> EntityView<Allocator>'
   14 | struct EntityView : public Entity<std::basic_string<char, std::char_traits<char>, Allocator>> {
      |        ^~~~~~~~~~
<source>:14:8: note:   template argument deduction/substitution failed:
<source>:19:24: note:   mismatched types 'EntityView<Allocator>' and 'int'
   19 |     EntityView myview{2};
      |                        ^

The problem seems to be that it is trying to do CTAD, however there's nothing to be derived from the constructor, also not in the inherited class. Is there any way to have the compiler just accept the default template argument instead of trying to deduce it?


Solution

  • Just define a universal deduction guide:

    #include <iostream>
    #include <stdexcept>
    #include <string>
    #include <memory>
    
    template <typename StringType = std::string>
    struct Entity {
        Entity(int) {
            std::cout << "Hello World!" << std::endl;
        }
        template <typename T>
        Entity(T) {
            std::cout << "template" << std::endl;
        }
        template<typename... Args>
        Entity(Args&&...) {
            std::cout << "variadic" << std::endl;
        }
    };
    
    template <typename Allocator = std::allocator<int>>
    struct EntityView : public Entity<std::basic_string<char, std::char_traits<char>, Allocator>> {
        using Entity<std::basic_string<char, std::char_traits<char>, Allocator>>::Entity;
    };
    
    template<typename... Args>
    EntityView(Args&&...) -> EntityView<>;
    
    int main() {
      { EntityView myview{2}; }
      { EntityView myview{2.}; }
      { EntityView myview{2, 3}; }
    }