Linker error for variational template

I have a variadic template program and a helper function:

#include <iostream>
#include <string>

using std::cout;

template<typename... Ts>
void fooImpl(char const *cp, Ts... args);

template<typename... Ts>
inline void foo(const std::string &s, Ts... args)
{
    fooImpl(s.c_str(), args...);
}

void fooImpl(char const *cp)
{
    // do something
}

template<typename T, typename... Ts>
void fooImpl(char const *cp, T val, Ts... args)
{
    char special{'@'};

    while (*cp)
    {
        if (*cp == special)
        {
            // handle val ...

            // recurse over remaining args
            fooImpl(cp, args...);
            return;
        }
        ++cp;
    }    
}

int main()
{
    std::string s = "Hello!";
    foo("Text", s, "C++", 3.14159, 42);
}

      

This gives a linker error:

/tmp/ccZpPMC2.o:vt-test.cc:

function void foo<std::string, char const*, double, int>(std::string const&, std::string, char const*, double, int)

: error:
undefined reference to ' void fooImpl<std::string, char const*, double, int>(char const*, std::string, char const*, double, int)

'

It compiles and links fine if I put the definition foo

down just before main()

. So I guess this is just a bug, but I am getting this with GCC 4.9 and clang 3.5, so maybe I am missing something?

+3


source to share


1 answer


template<typename T, typename... Ts>
void fooImpl(char const *cp, T val, Ts... args) { /* ... */ }

      

You are declaring an overload (!) Of the original template.

Since in the call

fooImpl(s.c_str(), args...);

      

there is a package extension in the argument list, unqualified-id denotes dependent name 1 . Dependent name resolution is applied. [Temp.dep.candidate]:

For a function call that depends on a template parameter, candidate functions are found using normal search rules (3.4.1, 3.4.2, 3.4.3), except that:

  • For the search part using unqualified name lookup (3.4.1) [..] only function declarations from the context of the template definition.

  • For the search portion using associated namespaces (3.4.2), only the function declarations found in the template definition are the context or context of the template instance.

It's safe to say that an unqualified name lookup will not find the second overload, since it only considers claims from the context of the template definition.

ADL applies here, but the global namespace will not be associated with any of the types in the options package . We have std::string, char const*, double, int

. [basic.lookup.argdep] / 2 indicates:



  • If T

    is a fundamental type, its associated namespace sets and classes are empty.

  • If T

    is a class type (including unions), its associated classes: the class itself; the class of which it is a member, if any; and these are direct and indirect base classes. Its associated namespaces are namespaces of which the associated classes are members. Moreover, if T

    is a class template specification, its associated namespaces and classes also include: namespaces and classes associated with template argument types supplied for template type parameters (excluding template template parameters); namespaces of which template template arguments are members; and the classes of which any Template members used as template template arguments are members.

Thus, neither fundamental types nor std::string

include the global namespace as an associated namespace.

Shortly speaking...

... the global namespace is not executed during ADL and the second overload was not found. Thus, the first function template overload is the only one found and subsequently fetched using overload resolution. The first overload is undefined, so a linker error is thrown.


1) [temp.dep] / 1:

In an expression like:

      postfix-expression (

list -expression opt)

where postfix expression is id-expression, id-expression denotes dependent name if

  • any of the expressions in the expression list is a package extension (14.5.3),
  • [..]
+5


source







All Articles