undefined reference to `bool Script::get<bool>(char const*)'

Рейтинг: 0Ответов: 2Опубликовано: 03.05.2015

Есть код(напиасан с использованием туториала https://eliasdaler.wordpress.com/2013/10/11/lua_cpp_binder/):

class Script{
    public:
        void create(const char *file);
        void close();
        void run();
        template <typename T> T get(const char *var);
    private:
        template <typename T> T lua_get(const string& variableName);
        template <typename T> T lua_get(const char *var){ return 0; }
        bool lua_gettostack(const string& variableName);
        lua_State *_L;
};

...

template <typename T> T EGE_Script::get(const char *var){
    if(!_L) {
        EGE_SetError("Script is not loaded");
        return "null";
    }

    T _result;
    if(lua_gettostack(string(var))){
        _result = lua_get<T>(string(var));
    } else {
        _result = "null";
    }

    return _result;
}

...

bool happy = Script.get<bool>("happy");

Не знаю, что не так? Вот что говорит компилятор:

main.cpp:(.text+0x56): undefined reference to `bool Script::get<bool>(char const*)'

Ответы

▲ 1Принят

Смотрите, в чём проблема.

Шаблонные методы — это не настоящие методы. Это лишь заготовка, из которой в момент инстанциации (то есть, в тот момент, когда компилятор читает get<bool>) производится настоящий метод.

В этот момент для создания метода требуется код. Если код недоступен, то компилятор не может создать метод, но он надеется, что вы знаете, что делаете.

Если вы положите шаблонный метод в .h, то во всех точках, где он будет вызываться, код будет виден. Если же вы перенесёте метод в .cpp, как это полагается делать для обычных методов, то в точке, где компилятор видит вызов конкретного шаблонного метода, у него не будет из чего составить тело метода.

Это неустранимое ограничение модели компиляции C++. Оно удостоилось даже упоминания в официальном C++ FAQ.

Поэтому решение проблемы — перенести реализацию метода в заголовочный файл.


Популярный workaround для случая, если вам очень-очень не хочется выносить код из .cpp-файла, который сработает, если вам наперёд известно, с какими типами будет инстанциирован шаблонный метод — оставить код в .cpp и вручную проинстанциировать все нужные шаблоны. Например, так, как описано в ответе @ixSci. Если ваш компилятор старый, сгодится такая конструкция:

namespace
{
    void dummy_instatiate()
    {
        Script* script = nullptr;
        script->get<bool>();
        script->get<int>();
        script->get<std::string>();
    }
}

(и никогда не вызывать эту функцию). Это заставит компилятор сгенерировать код для инстанциаций шаблона с параметром bool, int и std::string, так что с этими типами шаблоном можно будет пользоваться. Но не с другими.

▲ 1

Причину @VladD уже описал, я же предложу альтернативное решение данной проблемы. Т.е не workaround, а решение: Просто добавьте следующее в .cpp файл:

template bool Script::get<bool>(const char *var);

для каждого типа, что необходим. Это решит проблему с линковщиком.