Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Non-type template parameter with default value of type template parameter

I have the following code:

#include <iostream>
#include <string>
#include <type_traits>

struct Foo
{
   int i;
   int j;
};

template<typename T, T DEFAULT>
class Bar
{
public:
    Bar(): mVal(DEFAULT)
    {
        std::cout << "Bar constructor with mVal = " << mVal << "\n";
    }
    
    ~Bar(){}
    
    Bar(const T &i) : mVal(i)
    {
        std::cout << "Bar constructor with mVal = " << mVal << "\n";
    }
    
    Bar &operator=(T const &val)
    {
        mVal = val;
        std::cout << "Bar assignment operator with mVal = " << mVal << "\n";
        return *this;
    }
    
    explicit operator T() const
    {
        return mVal;
    }

private:
    T mVal;
};



int main()
{
  std::cout << "Hello \n";
  
  Bar<int, 10> bar1;
}

This is working fine in gcc C++14 as long as the first template parameter in Bar is of an integral type. If I want to do Bar<Foo, {}> the following error message is printed:

on-type template parameters of class type only available with '-std=c++2a' or '-std=gnu++2a'

I already expected that. Changing template<typename T, T DEFAULT> class Bar to template<typename T, T DEFAULT = {}> class Bar leads to the same error. Also a template specialization template<typename T> class Bar<T, {}> does not work for the same reason.

I also tried to experiment with std::enable_if_t<std::is_integral<T>::value> but could not find a solution that would work.

Is there any possible way to just write Bar<Foo> and not have to write a separate class like template<typename T, T DEFAULT> class BarDefault and template<typename T> class Bar for it?

like image 937
EmbedEngineer Avatar asked Jul 08 '20 08:07

EmbedEngineer


People also ask

Can template parameters have default values?

Just like in case of the function arguments, template parameters can have their default values. All template parameters with a default value have to be declared at the end of the template parameter list.

What are non-type parameter for templates?

A template non-type parameter is a template parameter where the type of the parameter is predefined and is substituted for a constexpr value passed in as an argument. A non-type parameter can be any of the following types: An integral type. An enumeration type.

Can we use non-type parameters as arguments template?

Non-type template arguments are normally used to initialize a class or to specify the sizes of class members. For non-type integral arguments, the instance argument matches the corresponding template parameter as long as the instance argument has a value and sign appropriate to the parameter type.

Which is correct example of template parameters?

For example, given a specialization Stack<int>, “int” is a template argument. Instantiation: This is when the compiler generates a regular class, method, or function by substituting each of the template's parameters with a concrete type.

What is a non-type template parameter?

A non-type template parameter must have a structural type, which is one of the following types (optionally cv-qualified, the qualifiers are ignored): the types of all base classes and non-static data members are structural types or (possibly multi-dimensional) array thereof.

What is a template parameter in C++?

A template type parameter is a placeholder type that is substituted for a type passed in as an argument. However, template type parameters are not the only type of template parameters available. Template classes and functions can make use of another kind of template parameter known as a non-type parameter.

What is the default value of the default template parameter?

The first template parameter is the type of elements the container stores and the second template parameter is the defaulted allocator a container of the standard template library has. Even the allocator has a default value such as in the case of a std::vector.

What is the difference between function templates and template arguments?

Each function template has a single function parameter whose type is a specialization of X with template arguments corresponding to the template parameters from the respective function template where, for each template parameter PP in the template parameter list of the function template, a corresponding template argument AA is formed.


1 Answers

Template parameters and template arguments - cppreference.com

A non-type template parameter must have a structural type, which is one of the following types (optionally cv-qualified, the qualifiers are ignored):

  • lvalue reference type (to object or to function);

  • an integral type;

  • a pointer type (to object or to function);

  • a pointer to member type (to member object or to member function);

  • an enumeration type;

  • std::nullptr_t; (since C++11)

  • a floating-point type;

  • a literal class type with the following properties:

  • all base classes and non-static data members are public and non-mutable and

  • the types of all bases classes and non-static data members are structural types or (possibly multi-dimensional) array thereof. (since C++20)

So basically custom structure as template value parameter is available since c++20.

Demo

You can overcome this problem by providing depending template which job is to provide a default value:

https://godbolt.org/z/RFp_xH

#include <iostream>
#include <string>
#include <type_traits>

struct Foo
{
   int i = 42;
   int j = 4;
};

std::ostream& operator<<(std::ostream& out, const Foo& a)
{
    return out << a.i << ',' << a.j;
}

template<typename T>
struct BarDefaultValue
{
    constexpr static T value()
    {
        return T{};
    }
};

template<>
struct BarDefaultValue<int>
{
    constexpr static int value()
    {
        return 42;
    }
};

template<typename T, typename D = BarDefaultValue<T>>
class Bar
{
public:
    Bar(): mVal(D::value())
    {
        std::cout << "Bar constructor with mVal = " << mVal << "\n";
    }
    
    ~Bar(){}
    
    Bar(const T &i) : mVal(i)
    {
        std::cout << "Bar constructor with mVal = " << mVal << "\n";
    }
    
    Bar &operator=(T const &val)
    {
        mVal = val;
        std::cout << "Bar assignment operator with mVal = " << mVal << "\n";
        return *this;
    }
    
    explicit operator T() const
    {
        return mVal;
    }

private:
    T mVal;
};

int main()
{
  std::cout << "Hello \n";
  
  Bar<int> bar1;
  Bar<Foo> bar2;
}
like image 77
Marek R Avatar answered Oct 18 '22 00:10

Marek R