3

I have such test.hpp:

#include <cstring>
#include <cassert>
#include <map>

template <typename T, typename Tag>
struct Boo {
  using ElementType = T;
  static const char name[];
};

struct CFG_ELM_NAME__Pref1 {};
using Pref1 = Boo<int, CFG_ELM_NAME__Pref1>;

struct Foo {
  template <typename CfgElm>
  void get() const {
    auto it = cache_.find(CfgElm::name);
    assert(it != cache_.end());
  }
  Foo();

private:
  struct CmpCStr final {
    bool operator()(const char *a, const char *b) const {
      return std::strcmp(a, b) < 0;
    }
  };

  using PrefCacheMap = std::map<const char *, int, CmpCStr>;
  PrefCacheMap cache_;
};

and use it like this (test2.cpp):

#include "test.hpp"

void f()
{
    Foo foo;
    foo.get<Pref1>();
}

and initialize it like this (test.cpp):

#include "test.hpp"

template <> const char Pref1::name[] = "Pref1";

Foo::Foo()
{
    cache_.insert(std::make_pair(Pref1::name, 17));
}

This is reduced example, so Foo::get do nothing.

clang produces such warning for this code:

clang++ -Wall -Wextra -std=c++11 test.cpp test2.cpp
In file included from test2.cpp:1:
./test.hpp:15:35: warning: instantiation of variable 'Boo<int,
      CFG_ELM_NAME__Pref1>::name' required here, but no definition is
      available [-Wundefined-var-template]
    auto it = cache_.find(CfgElm::name);
                                  ^
test2.cpp:6:6: note: in instantiation of function template specialization
      'Foo::get<Boo<int, CFG_ELM_NAME__Pref1> >' requested here
        foo.get<Pref1>();
            ^
./test.hpp:7:21: note: forward declaration of template entity is here
  static const char name[];
                    ^
./test.hpp:15:35: note: add an explicit instantiation declaration to
      suppress this warning if 'Boo<int, CFG_ELM_NAME__Pref1>::name' is
      explicitly instantiated in another translation unit
    auto it = cache_.find(CfgElm::name);

This codes does compiles and links without problems. The only problem is warning. I have no idea how to suppress it.

I found this question explicit instantiation of static variable of a template class in different translation units , but I can not use provided solution, because of I don't know template arguments.

I can not write: template<> int Boo<Type1, Type2>::name; because of the whole idea is to use my code like this: foo.get<Pref1>(), without explicitly pointing that Pref1 is Boo<int, CFG_ELM_NAME__Pref1>.

So anybody know how to suppress warning, without turn of such warning for whole project via -Wno-undefined-var-template?

SU3
  • 5,064
  • 3
  • 35
  • 66
user1244932
  • 7,352
  • 5
  • 46
  • 103

1 Answers1

4

You should add Boo::name template definition in the same header file:

   static const char * name;
}; // End Boo 

template<typename T, typename Tag> 
const char * Boo<T, Tag>::name{};

Update: now it is clarified that you are trying to write specialization for name in some translation unit after instantiating it in header file. This will require some trickery. You need to declare specialization in header file before instantiating it and probably use external template and explicitly instantiate it in the same translation unit as name:

// header

template <typename T, typename Tag> struct Boo {
  using ElementType = T;
  static const char * name;
};

struct CFG_ELM_NAME__Pref1 {};

// indicate that name for this specialization exists elsewhere
template<> const char * Boo<int, CFG_ELM_NAME__Pref1>::name;

// indicate that template is defined somewhere
extern template struct Boo<int, CFG_ELM_NAME__Pref1>;

using Pref1 = Boo<int, CFG_ELM_NAME__Pref1>;

// test.cpp

// definition will be present only in this translation unit
template<> const char * Boo<int, CFG_ELM_NAME__Pref1>::name{"Pref1"};

// explicit instantiation
template struct Boo<int, CFG_ELM_NAME__Pref1>;

online compiler

user7860670
  • 35,849
  • 4
  • 58
  • 84
  • It works, but I had to remove `*` and add `[]{}` to make it compiles without errors. But what means `{}` at the end, initialization? But I init `name` in other `.cpp` not show in my question. – user1244932 Jan 25 '18 at 21:27
  • @user1244932 `{}` at the end is a [direct list initialization](http://en.cppreference.com/w/cpp/language/list_initialization). Initializing `name` in `.cpp` is not a good idea, see [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). – user7860670 Jan 25 '18 at 21:39
  • I do not initialize whole template in cpp, I intialize concrete type, like this `template <> const char Pref1::name[] = "Pref1";` – user1244932 Jan 25 '18 at 22:10
  • I 've updated my question to show how I initialize `name`. I tried your solution and it works fine with `clang++`, but `g++ -Wall -Wextra -std=c++11 test.cpp test2.cpp` return `multiple definition of name` during link time, but compiles fine without `template const char * Boo::name{};` I used clang 5.0.1 and gcc 7.2.1. – user1244932 Jan 25 '18 at 22:21