3

I found an example where the output is different depending on the optimization settings (-O3 vs none), yet GCC 4.8.2 produces no warnings, even with the -std=c++11 -Wall -pedantic options.

In this particular case, I'm assuming that "forgetting" the commented line in header.h is a mistake, and with -O3, c<int>::get() gets inlined.

However, is there any way to protect yourself against these kinds of mistakes -- a compiler or linker option perhaps?

header.h:

#ifndef HEADER_H
#define HEADER_H

template<class T>
struct c
{
   int get() { return 0; }
};

// template<> int c<int>::get();

#endif

imp.cpp:

#include "header.h"

template<> 
int c<int>::get()
{
   return 1;
}

main.cpp:

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

int main()
{
    c<int> i;
    std::cout << i.get() << '\n'; // prints 0 with -O3, and 1 without
}

build:

c++ -std=c++11 -pedantic -Wall -O3 -c imp.cpp
c++ -std=c++11 -pedantic -Wall -O3 -c main.cpp
c++ -std=c++11 -pedantic -Wall -O3 imp.o main.o
MWB
  • 11,740
  • 6
  • 46
  • 91

2 Answers2

6
  1. What you get if you have the line in your header-file, is a declaration of an explicit specialization for that member-function.

    Thus, main.cpp is assured of a definition in some other compilation-unit, and things work.

  2. If you leave it out, you have a violation of the ODR:

    That specific instantiation of your class-template is different in the compilation-units. Resulting in "ill-formed, no diagnostic required", so anything goes.

    And there is (currently?) no compiler-option to force gcc to diagnose it.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
Deduplicator
  • 44,692
  • 7
  • 66
  • 118
0

The true mistake here is the way you're laying out your source files and building them. When c<int>::get() is used, its definition should be available in order to instantiate the template. To fix this, header.h should #include "imp.cpp" rather than the other way around. You may want to rename imp.cpp to imp.inl or something else.

When you define templates which are used outside of a single .cpp file, those definitions should be visible to anyone who includes their header.

As an aside: I don't think there's any way to make the compiler or linker warn you about what you've done here. But if you structure your project as I've described, this problem won't happen, because the forward declaration will be unnecessary.

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • 1
    *When you define templates which are used outside of a single .cpp file, those definitions should be visible to anyone who includes their header.* Wouldn't the last (4th) snippet from http://stackoverflow.com/a/495056/1937197 contradict this general rule? – MWB Feb 06 '15 at 01:17
  • @MaxB: explicit template instantiation is an advanced use case which most people don't care about. You're right that it's an exception to the general rule...but one which can be safely ignored because once one moves to an explicit instantiation model, one will know to move everything out of the headers (not half of everything as in the OP). – John Zwinck Feb 06 '15 at 01:22