10

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?

0

3 Answers 3

6

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):

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;
}
Sign up to request clarification or add additional context in comments.

Comments

0

You could make a default value to supply.

template<class T>
constexpr T brace_init_value{};

Then used as:

template<typename T, T DEFAULT = brace_init_value<T> >
class Bar
{

3 Comments

Thank you for your answer but unfortunately this is not compiling in C++14
@EmbedEngineer Why not? What is the compiler and the full error?
the error stays the same on-type template parameters of class type only available with '-std=c++2a' or '-std=gnu++2a compiler version is gcc (Ubuntu 9.2.1-9ubuntu2) 9.2.1 20191008 with "-std=gnu++14" flag
0

Thanks to @Marek R for his idea.

My solution to the problem is the following code which is compiling with gcc in C++14:

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

template <typename T>
constexpr typename std::enable_if<std::is_class<T>::value, T>::type BarDefaultValue(const int &)
{
    return {};
}

template <typename T>
constexpr typename std::enable_if<std::is_integral<T>::value, T>::type BarDefaultValue(const int &ret)
{
    return static_cast<T>(ret);
}

struct Foo
{
   int i;
   int j;
};

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

template<typename T, int DEFAULT = 0>
class Bar
{
public:
    Bar(): mVal(BarDefaultValue<T>(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;
  Bar<Foo> bar2;
}

The output of this file is:

Hello 
Bar constructor with mVal = 10
Bar constructor with mVal = 0,0

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.