samedi 1 août 2015

Inline function is not defined

Using Variant and Visitor idioms for dealing with abstract syntax tree of mathematical expression I faced linker errors undefined reference to 'function_name' prepended with warnings inline function 'function_name' is not defined [-Wundefined-inline], where function_name is all possible names of specializations (i.e. for B and for C) of function marked // 1 below (live example):

#include <type_traits>
#include <utility>
#include <iostream>

#include <boost/variant.hpp>

#include <cstdlib>

namespace
{

struct A {};
struct B {};
struct C {};

using V = boost::variant< A, B, C >;

struct visitor
{

    using result_type = void;

    result_type
    operator () (V v) const
    {
#if 1
        return boost::apply_visitor([this] (auto && x) -> result_type { return apply(std::forward< decltype(x) >(x)); }, v);
    }
#else
        return boost::apply_visitor(*this, v);
    }

    template< typename T >
    result_type
    operator () (T && x) const
    {
        return apply(std::forward< T >(x));
    }
#endif

private :

    result_type
    apply(A) const
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }

    template< typename T >
    result_type
    apply(T &&) const // 1
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }

public : // !

};

}

int
main()
{
    visitor{}(V{B{}});
    return EXIT_SUCCESS;
}

Frankly, warning message here is function 'function_name' has internal linkage but is not defined [-Wundefined-internal], but if we split above code into header and .cpp files, then message would be exactly as one mentioned above.

If I change #if 0 to #if 1, then all compiles fine. Another note is: when I move result_type operator () (V v) const to second public section (marked // !), then all compiles fine too.

Seems lambda makes something subtle with instantiation of function template defined strictly below definition of lambda.

How to explain above behaviour and difference in behaviour of lambda version and version with visitor & passed to boost::apply_visitor?

Real-life example is here.

Aucun commentaire:

Enregistrer un commentaire