10

I would like to put the implementation of the specializations of a templated function to a separate source file, but if I try to call it (in MyAction), I get this error:

Explicit specialization has already been instantiated

I have no idea, why I get this error. Example code:

main.cpp

#include <iostream>
#include <string>

#include "MyClass.h"

int main()
{
    std::cout << "Hello, " << XX::MyClass().MyMethod<1>() << std::endl;
    std::cin.get();
}

MyClass.h

#pragma once

#include <string>

namespace XX {

    struct MyClass {

        std::string MyAction() {
            return MyMethod<0>() + MyMethod<1>();
        }

        template<int>
        std::string MyMethod();

    };

    template<>
    std::string MyClass::MyMethod<0>();

    template<>
    std::string MyClass::MyMethod<1>();

}

MyClass.cpp

#include "MyClass.h"

namespace XX {

    template<>
    std::string MyClass::MyMethod<0>() {
        return "FOO";
    }

    template<>
    std::string MyClass::MyMethod<1>() {
        return "BAR";
    }

}

Is there a template instantiation rule, which I am not aware of?

Iter Ator
  • 8,226
  • 20
  • 73
  • 164
  • VisualStudio 2017 – Iter Ator Jul 26 '19 at 13:03
  • https://godbolt.org/z/ZmpFCR – Marek R Jul 26 '19 at 13:13
  • I don't think you can put template functions in a cpp-file and call them from another translation unit. As far as I know, template functions need to either be in a header file or directly in the cpp-file where they're called. – rashmatash Jul 26 '19 at 13:14
  • @rashmatash not necessarily. If you list every instanciation that will be used, there is nothing preventing you to put your templates in a CPP. However, this is not always possible. – Guillaume Racicot Jul 26 '19 at 13:18
  • 1
    Yes he can. There must be at least on translation unit which defines specific instrumentation of template and other translation units can use it without seeing definition. – Marek R Jul 26 '19 at 13:18
  • I recall having seen a similar issue around here (also with MSVC) which was fixed by doing something like `template<> extern std::string MyClass::MyMethod<1>();`. But I'm not sure if it was this exact problem. – Max Langhof Jul 26 '19 at 13:33
  • According to https://stackoverflow.com/questions/53271119/explicit-instantiation-of-function-template-specialization, your code is correct btw. It's likely an MSVC problem/bug. – Max Langhof Jul 26 '19 at 13:37
  • @MaxLanghof I've provided a godbolt link, gcc fails too. I checked also a clang and it fails. In link you have provided there is no usage of template before specialization was defined. – Marek R Jul 26 '19 at 14:07
  • @MarekR Oh, you're totally right. My bad! – Max Langhof Jul 26 '19 at 14:19

1 Answers1

10

Ok looks like problem is an order.

When you are defining MyAction compiler tries instantiate template, but he doesn't know about specialization.

When you declare MyAction and define it in cpp after template specialization it will work.

// header part
#include <string>

namespace XX {
    struct MyClass {
        template<int>
        std::string MyMethod();
        std::string MyAction();
    };
}

// cpp part
namespace XX {
    template<>
    std::string MyClass::MyMethod<0>() {
        return "a";
    }

    template<>
    std::string MyClass::MyMethod<1>() {
        return "b";
    }

    std::string MyClass::MyAction() {
        return MyMethod<0>() + MyMethod<1>();
    }
}

See here: https://godbolt.org/z/aGSB21

Note if you move MyClass::MyAction() above MyClass::MyMethod<1>() error will come back.

Here is version where specialization is can be declared in header file: https://godbolt.org/z/kHjlne

Marek R
  • 32,568
  • 6
  • 55
  • 140