Zebrafish
Zebrafish

Reputation: 13886

Why can't this template argument be deduced?

The parameters for Foo have default values, therefore in main() I can do Foo(); to create a Foo and it'll have the default template arguments. However I cannot use in a template argument:

template <typename T = double, int a =2, int b = 3>
struct Foo {};


//cannot deduce template arguments for ‘Foo’ from ()
template <typename FooType = Foo()>   // <-- Error
struct TemplatedStructTakingAFooType
{

};

int main()
{
    Foo(); // Foo type is deduced to be default values, ie.,
    //Foo<double, 2, 3>;
    decltype(Foo()); // Compiler knows the type
}

In my Visual Studio compiler it highlights the area in red indicating an error, but compiles. On onlineGDB under C++17 is fails to compile with the above error. Is this allowed? Is there any reason it shouldn't be?

Edit: I realised how stupid using is as Foo() isn't a type, but neither '= Foo' nor '= decltype(Foo())' work either.

Upvotes: 2

Views: 209

Answers (3)

Chris Uzdavinis
Chris Uzdavinis

Reputation: 6131

Something will have to change because you're mixing ideas.

You can pass Foo as either

  1. a type parameter,
  2. a template parameter
  3. a user defined non-type template parameter (New in C++20)

Here's an example of each:

template <typename T = double, int a =2, int b = 3>
struct Foo {};

// As a type.  The caller has secided what the template arguments that Foo
// will have, or by allowing them to default, has chosen the defaults.
template <typename FooType = decltype(Foo{})>
struct A
{
};

// Here FooType is passed as a template.  Inside B, you'd need to 
// provide it template parameters to instantiate it.
template <template <typename T, int, int> typename Foo2 = Foo>
struct B
{
    using F = Foo2<float, 3, 4>;
};

// As a user-defined non-type template parameter
// This only works in C++20, since it's a new feature
template <Foo f>
struct C
{
};

// And finally, CTAD to with deduced arguments for Foo as a user-defined
// non-type tempalte parameter, with friendly constructor syntax.  (For the
// caller, not for the reader of this monstrosity).
// The deduction guide allows passing an instance of Foo<T, a, b> to the
// constructor and allowing it to match types for T, a, b for the struct.
template <typename T, int a, int b, Foo<T, a, b> f>
struct D
{
    D(Foo<T, a, b>) { }
};
template <typename T, int a, int b> D(Foo<T, a, b>) -> D<T, a, b, Foo<T, a, b>{}>;

int main()
{
    A a;                      // default
    A<Foo<float, 5, 9>> a2;   // give types to Foo given to A

    B<Foo> b;                 // Pass Foo to b as a template-template param

    C<Foo{}> c;               // Pass instance of Foo with default args

    Foo<short, 99, 0> customFoo;
    D d(customFoo);           // Pass instance to constructor(!) to 
                              // set template args even when Foo is not default.
}

See it live: https://godbolt.org/z/8MGcn6zjj

Upvotes: 0

nhatnq
nhatnq

Reputation: 1193

Foo() is not a type so need decltype:

typename FooType = decltype(Foo())

Upvotes: 3

Some programmer dude
Some programmer dude

Reputation: 409146

The problem is that Foo isn't a type, it's a template of a type.

You need to specify an actual type here, which needs the template angle-brackets, Foo<> which is the type Foo<double, 2, 3>:

typename FooType = Foo<>

Upvotes: 5

Related Questions