0

I have the following makefile to build the following few files.

FLAGS = -O3

release: main Collection
    g++ -o main Collection.o main.o $(FLAGS)

main: main.cpp
    g++ -c main.cpp -std=c++17 $(FLAGS)

Collection: Collection.cpp Collection.hpp
    g++ -c Collection.cpp -std=c++17 $(FLAGS)

clean: main
    rm -f main *.o
/// Collection.hpp

#include <unordered_map>

struct RecallAsIs {
  static constexpr int val = 0;
};

template <int value>
struct RecallAdd {
  static constexpr int val = value;
};

class Collection {
 public:
  Collection();

  ~Collection();

  void Add(int key, int value);

  template <class RecallPolicy = RecallAsIs>
  int Get(int key) const;

 private:
  std::unordered_map<int, int> values_;
};

template <class RecallPolicy>
int Collection::Get(int key) const {
  return Get(key) + RecallPolicy::val;
}
/// Collection.cpp

#include "Collection.hpp"

Collection::Collection() = default;

Collection::~Collection() = default;

void Collection::Add(int key, int value) { values_[key] = value; }

template <>
int Collection::Get<RecallAsIs>(int key) const {
  return values_.at(key);
}
/// main.cpp

#include <iostream>

#include "Collection.hpp"

int main() {
  Collection c;
  c.Add(10, 12);
  std::cout << c.Get(10) << std::endl;
  return 0;
}

When turning off optimization, ie. setting FLAGS = -O0, 12 is printed to the terminal. However, when setting FLAGS = -O3, the program ends up hanging and I can't quite figure out why it's hanging. My latest running theory is that in the general implementation of Collection::Get in the hpp is inlined by the compiler when optimization is enabled and doesn't see the specialization thus calls itself recursively which leads to the program hanging. Is my analysis correct?

tree
  • 229
  • 3
  • 12
  • It is logically impossible for the compiler to "see the specialization" in `Collection.cpp` when compiling `main.cpp` file. C++ does not work this way. At the very least you must declare the specialization in the header file. As is, this is undefined behavior, and violation of the One Definition Rule. – Sam Varshavchik Sep 22 '20 at 02:11
  • Does this answer your question? [Why can templates only be implemented in the header file?](https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) – JaMiT Sep 22 '20 at 02:19
  • I understand what @SamVarshavchik said, but I guess the bigger question is why is there a difference in behavior between optimization levels. – tree Sep 22 '20 at 02:20
  • Yes, compilers need visibility of explicitly specialised template functions (at minimum, a declaration that informs it the specialisation is defined elsewhere). If my memory is correct, what you're doing (calling a templated function in one compilation unit with no visibility of the explicit specialisation, and defining the specialisation in another compilation unit) gives undefined behaviour - which is perfectly consistent with code optimisation affecting behaviour (optimisation strategies - including for inlining - often start with an assumption that there is no undefined behaviour). – Peter Sep 22 '20 at 02:20
  • @tree If behavior changes depending on optimization level, chances are good that you have [undefined behavior](https://stackoverflow.com/questions/2397984/undefined-unspecified-and-implementation-defined-behavior). Speculating about precisely how the observed behavior came about sometimes has academic interest, but it lacks practical utility. If your interest is academic instead of practical, your question should clearly state that (and be aware that speculation is dangerously close to "opinion-based"). – JaMiT Sep 22 '20 at 03:02

1 Answers1

2

This program is ill-formed, no diagnostic required.

[temp.expl.spec]/6 If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required...

Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85
  • 1
    So the fact that it works "correctly" when optimizations are disabled is just sheer luck/dependent on the compiler implementation? – tree Sep 22 '20 at 02:20
  • Yes. In that case [nasal demons simply flew out of the desired nostril](https://en.wiktionary.org/wiki/nasal_demon). – Sam Varshavchik Sep 22 '20 at 11:02