0

I've taken the project I'm working on and shortened it to three brief files that replicate what's happening. I'm working with the Code::Blocks IDE using g++ under the 2011 standard.

The files are main.cpp, thing.cpp and thing.h. The header (.h) has declarations and the source (.cpp) has implementations. The thing files define a class Thing with a template parameter T. All Thing does is hold an object of type T (it basically does nothing).

main.cpp:

#include <iostream>
#include "thing.h"

int main(){
    Thing<int> a(9);
    Thing<double> b(3.2);
    auto c = a + b;
    c.display();

    return 0;
}

thing.h:

#ifndef THING_H
#define THING_H

template<typename T>
class Thing{
private:
    T content;
public:
    Thing(const T& content);
    ~Thing(){};

    T get_content() const;

    void display();
};

template<typename S, typename T>
auto operator+(const Thing<S>& s, const Thing<T>& t);

#endif // THING_H

thing.cpp:

#include "thing.h"
#include <iostream>

template<typename T>
Thing<T>::Thing(const T& content)
:content(content){}

template<typename T>
void Thing<T>::display(){
    std::cout << content << '\n';
}

template<typename T>
T Thing<T>::get_content() const {
    return content;
}

template<typename S, typename T>
auto operator+(const Thing<S>& s, const Thing<T>& t){
    S s_content = s.get_content();
    T t_content = t.get_content();
    Thing<typename std::common_type<S, T>::type> sum = s_content + t_content;
    return sum;
}

Here's the strange behavior: the code will compile or not depending on the line #include "thing.h". Using thing.h here will not compile, causing an error:

error: use of 'auto operator+(const Thing<S>&, const Thing<T>&) [with S = int; T = double]' before deduction of 'auto'
error: invalid use of 'auto'

Changing this line to #include "thing.cpp" allows the code to compile without problems and functions as intended (it outputs 12.2 to the console).

My question is: What is the compiler doing differently in the two cases? How can I change the code to eliminate this error when including the header?

Thanks in advance.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Johan
  • 343
  • 1
  • 4
  • 13
  • 4
    You should probably also ask yourself [why you're not implementing your templates in a header](https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) in the first place? There is no way a caller can possibly deduce the `auto` given only `auto operator+(const Thing& s, const Thing& t);` to work with. It needs the code. – WhozCraig May 25 '17 at 19:43
  • @WhozCraig Why does the caller only have access to the header and not the implementation in the source file? In fact, how does a caller access the source in other situations? My impression was that when the function is called, the compiler will substitute in the implementation, in which the auto return type can be deduced. – Johan May 25 '17 at 19:56
  • Compiler can't do that because it needs the template definition at hand when you're about to use it. Main is a translation unit, Thing is another one, and the template class definition of `Thing` doesn't exist in main TU. – Mário Feroldi May 25 '17 at 20:06
  • @StoryTeller: No, that is not the cause. Please be more careful with your dupehammer. – Lightness Races in Orbit May 25 '17 at 20:19
  • @MárioFeroldi I think I understand the problem. I was under the impression that the `auto` deduction was causing the problem, whereas it's really the template shenanigans. Removing the line `auto c = a + b;` causes new, different errors (`undefined reference to Thing::Thing(etc...)`). I thought the `auto` was to blame because these new errors did not show up when it was included, and the build process will usually spit out a list of *all* the errors in the code. – Johan May 25 '17 at 20:25

1 Answers1

0

Deduced return types were introduced in C++14.

Coliru reproduces your problem in C++11 mode, but shows the code working in C++14.

And, of course, although this wasn't the immediate cause of your problem, it is likely that you will have to move your template definitions into the/a header.

Oh, and here's the minimal testcase you should have been testing with:

template <typename T>
auto foo(T a)
{
    return a;
}

int main()
{
    foo(42);
}
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • The code worked for the OP, by their own admission, when `thing.cpp` was included **instead** of `thing.h`. So it's not a C++11 vs C++14 issue. It is a template being defined not inside a header issue. Which makes only the link in your answer relevant. – StoryTeller - Unslander Monica May 25 '17 at 20:42