简体   繁体   中英

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 .

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);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM