Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variadic template functions in namespace and in class

I defined three variadic template functions in class as static methods. Than I decided to move them to namespace. Namespace-approach fails to compile while class-solution compiles and works as expected.

Here is working class - based solution:

#include <list>
#include <string>

class sample
{
public:
    template<typename T>
    static std::string encode(T t) {
        /* do something useful with t */
        return std::string("encoded value");
    }

    template<typename... Ts>
    static std::string encode(Ts... ts) {
        std::list<std::string> values;
        return encode(values, ts...);
    }

    template<typename T, typename... Ts>
    static std::string encode(std::list<std::string>& values, T t, Ts... ts) {
        values.push_back(encode(t));
        return encode(values, ts...);
    }
};

Here are similar definitions within namespace:

#include <list>
#include <string>

namespace sample
{
    template<typename T>
    std::string encode(T t) {
        /* do something useful with t */
        return std::string("encoded value");
    }

    template<typename... Ts>
    std::string encode(Ts... ts) {
        std::list<std::string> values;
        return encode(values, ts...);
    }

    template<typename T, typename... Ts>
    std::string encode(std::list<std::string>& values, T t, Ts... ts) {
        values.push_back(encode(t));
        return encode(values, ts...);
    }
};

In both cases encode is used in the following way:

std::string encoded = sample::encode(1, 2u, 3.0);

Namespace-approach fails with following error (first line shortened):

sample_namespace.hpp: In instantiation of ‘std::__cxx11::string sample::encode(Ts ...) [with Ts = {std::__cxx11::list<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, std::__cxx11::list<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, std::__cxx11::list<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, std::__cxx11::list<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, std::__cxx11::list<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, std::__cxx11::list<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, std::__cxx11::list<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, std::__cxx11::list<std::__cxx11::basic_string<char, std::char_traits<char>, ...
sample_namespace.hpp:18:22:   recursively required from ‘std::__cxx11::string sample::encode(Ts ...) [with Ts = {std::__cxx11::list<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, int, unsigned int, double}]’
sample_namespace.hpp:18:22:   required from ‘std::__cxx11::string sample::encode(Ts ...) [with Ts = {int, unsigned int, double}]’
sample_namespace.hpp:17:32: fatal error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum)
     std::list<std::string> values;
                            ^~~~~~
compilation terminated.

The same error occurs when encode(int, uint, double) is defined explicitly:

template std::string sample::encode<int, uint, double>(int a, uint b, double c);

When single argument is given first template is encode(T t) used and code compiles.

Why templates placed within namespace fails?

Above code was (not)compiled with gcc 6.4.0 x86_64. I also tried to compile it with C++14 and C++17 enabled.
Code fails to compile with gcc 7.3, clang 6.0.0 and icc 18 too - checked via godbolt.org.

like image 406
ptrlukas Avatar asked Jan 01 '26 00:01

ptrlukas


1 Answers

Just need to move the variadic template form of encode before the non-variadic template form. When you're dealing with non-member functions, the order matters much more than it does for member functions. Godbolt accepts the following:

#include <list>
#include <string>

namespace sample
{
    template<typename T>
    std::string encode(T t) {
        /* do something useful with t */
        return std::string("encoded value");
    }

    template<typename T, typename... Ts>
    std::string encode(std::list<std::string>& values, T t, Ts... ts) {
    values.push_back(encode(t));
        return encode(values, ts...);
    }

    template<typename... Ts>
    std::string encode(Ts... ts) {
        std::list<std::string> values;
        return encode(values, ts...);
    }
};

std::string do_it() {
    return sample::encode(1, 2u, 3.0);
}
like image 200
Stephen Newell Avatar answered Jan 03 '26 13:01

Stephen Newell



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!