C++11 extern templates: where do we actually need them? - c++11

In C++03 we have template explicit instantiation definitions (template class Foo<int>) which force instantiation of a template class.
In C++11 we've got template explicit instantiation declarations (extern template class Foo<int>) which should prevent implicit instantiations of a template class. (Class template instantiation)
I'm trying to simulate the situation where I actually needed the explicit instantiation declaration in order to reduce compilation time. But I can't. Looks like everything works without this feature (or doesn't work with it).
Here is an example:
//Foo.h
#pragma once
template<class T>
class Foo
{
T inst;
public:
Foo(T i);
T& get() const;
};
//Foo.cpp
#include "stdafx.h"
#include "Foo.h"
template<class T>
Foo<T>::Foo(T inst) : inst(inst) { }
template<class T>
T& Foo<T>::get() const { return inst; }
template class Foo<int>; //explicit instantiation definition
//test1.h
#pragma once
#include "Foo.h"
//This line does not work
//extern template class Foo<int>; //explicit instantiation declaration.
void baz();
//test1.cpp
#include "stdafx.h"
#include "test1.h"
void baz()
{
Foo<int> foo(10);
int i = foo.get();
}
The result does not depend on whether I comment (extern template class Foo<int>;) line or not.
Here is symbols of both *.obj files:
dumpbin /SYMBOLS test1.obj
011 00000000 UNDEF notype () External | ??0?$Foo#H##QAE#H#Z (public: __thiscall Foo::Foo(int))'
012 00000000 UNDEF notype () External | ?get#?$Foo#H##QBEHXZ (public: int __thiscall Foo::get(void)const )
013 00000000 SECT4 notype () External | ?baz##YAXXZ (void __cdecl baz(void))
...
dumpbin /SYMBOLS Foo.obj
017 00000000 SECT4 notype () External | ??0?$Foo#H##QAE#H#Z (public: __thiscall Foo::Foo(int))
018 00000000 SECT6 notype () External | ?get#?$Foo#H##QBEHXZ (public: int __thiscall Foo::get(void)const )
Pay attention what Foo<int>::Foo<int>(int) and int Foo<int>::get(void)const marked as UNDEF in test1.obj which means that they must be resolved elsewhere (i.e. Foo was compiled only ONCE).
ATTEMP #2:
If I define full template in Foo.h file (without explicit instantiation definition) then extern template doesn't help - template compiles twice (in both test1.cpp and test2.cpp).
Example:
//test1.h
#pragma once
#include "Foo.h"
void baz();
//test1.cpp
#include "stdafx.h"
#include "test1.h"
void baz()
{
Foo<int> foo(10); //implicit instantiation of Foo<int>
int i = foo.get();
}
//test2.h
#pragma once
#include "Foo.h"
extern template class Foo<int>;
void bar();
//test2.cpp
#include "stdafx.h"
#include "test2.h"
void bar()
{
Foo<int> foo(10); //should refer to Foo<int> from test1.obj but IT IS NOT
int i = foo.get();
}
Here are symbol dumps:
dumpbin /SYMBOLS test2.obj
01D 00000000 SECT4 notype () External | ??0?$Foo#H##QAE#H#Z (public: __thiscall Foo::Foo(int))
01E 00000000 SECT8 notype () External | ?get#?$Foo#H##QBEHXZ (public: int __thiscall Foo::get(void)const )
01F 00000000 SECT6 notype () External | ?bar##YAXXZ (void __cdecl bar(void))
dumpbin /SYMBOLS test1.obj
01D 00000000 SECT6 notype () External | ?baz##YAXXZ (void __cdecl baz(void))
01E 00000000 SECT4 notype () External | ??0?$Foo#H##QAE#H#Z (public: __thiscall Foo::Foo(int))
01F 00000000 SECT8 notype () External | ?get#?$Foo#H##QBEHXZ (public: int __thiscall Foo::get(void)const )
In both *.obj files Foo presents.
So my question is in what may be the usefulness of the explicit instantiation declarations? Or maybe I miss something in my tests?
I use VS2013 compiler.

Here is a good explanation why the ATTEMP#2 does not work as I want:
Is there a bug with extern template in Visual C++?
In short when you define and implement the template in header file the compiler may inline it. And then it does the explicit instantiation definition doesn't work by standart (14.7.2/10 "Explicit instantiation").
So we need to force the compiler to NOT inline the template. For example by implementing it just after the declaration.
template<class T>
class Foo {
...
T get() const;
};
template<class T>
T Foo<T>::get() const
{ ... }

Related

C++11 linker error VS17

#include <iostream>
#include <fstream>
#include <type_traits>
#include <Windows.h>
#include "abase.h"
using namespace std;
class Storage {
string _path;
public:
Storage(string path);
~Storage() = default;
template <typename T >
bool writeFile(string fileName,
typename enable_if<is_base_of<ABase, T>::value, T >::type* data);
}
Definition...
#include "storage.h"
Storage::Storage(string path)
{
this->_path = path;
}
template <typename T >
bool Storage::writeFile(string fileName,
typename enable_if<is_base_of<ABase, T>::value, T >::type* data){
return true;
}
Im still getting error by Linker:
LNK2019 unresolved external symbol "public: bool __thiscall
Storage::writeFile(class std::basic_string,class std::allocator >,class AFile*)"
(??$writeFile#VAFile###Storage##QAE_NV?$basic_string#DU?$char_traits#D#std##V?$allocator#D#2##std##PAVAFile###Z)
referenced in function _main
Why am i getting it if code is looking right. Its generic definition of method in a class and Im trying to constrain class type passing to the method.
And the AFile is inherited from ABase.
ABase is abstract class.
Simple usage in main:
Storage* s = new Storage("C:\\aPath...");
AFile* afile = new AFile();
s->writeFile<AFile>("a.txt", afile);
To solve your linking error you can explicitly instantiate[1] your template member function in the Storage.cpp like this:
template
bool Storage::writeFile<AFile>(string fileName,
enable_if<is_base_of<ABase, AFile>::value, AFile>::type* data);
so the compiler creates the function and the linker can find.
It's better to move the definitions in the header file - Why can templates only be implemented in the header file?.

boost fusion why there is different result in c++11 and c++03?

Why the following type as_vet_type is boost::fusion::vector2<const int, const int> when compiling with C++03 and boost::fusion::vector<int, int> when compiling with c++11 ? const is missing with c++11. Is this a bug or feature ?
I tested this with boost 1.60.
#include <boost/fusion/container/vector.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/container/vector/vector_fwd.hpp>
#include <boost/fusion/include/vector_fwd.hpp>
#include <boost/fusion/algorithm/transformation/transform.hpp>
#include <boost/fusion/include/transform.hpp>
#include <boost/fusion/container/vector/convert.hpp>
#include <boost/fusion/include/as_vector.hpp>
struct functor
{
template<class> struct result;
template<class F, class T>
struct result<F(T)> {
typedef const int type;
};
template<class T>
typename result<functor(T) >::type
operator()(T x) const;
};
int main()
{
typedef boost::fusion::vector<const int & ,char &> cont_type;
typedef typename boost::fusion::result_of::transform<cont_type ,functor >::type view_type;
typedef typename boost::fusion::result_of::as_vector<view_type>::type as_vec_type;
as_vec_type asd;
asd.x;
return 0;
}
I got a comment from someone but unfortunetly it is no longer visible :(
Anyway thanks to that comment i figured out what is happenning.
It turns out that this issue is related boost::result_of and not to boost::fusion.
boost::result_of can behave diffrently in c++11 when decltype is used and in c++03.
boost::result_of documentation describes this diffrence in part "Non-class prvalues and cv-qualification".
I can provide this simplified explanation.
In C++11 , in this function declaration: const int f(); const is simply ignored by compiler and f signature becomes int f(); and thats why decltype(const int f()); is int.
GCC 5.3.2 will even produce the following warning if you declare const int f();
prog.cc:5:13: warning: type qualifiers ignored on function return type
[-Wignored-qualifiers] const int f()

Visual Studio cannot find my methods despite them being spelled correctly

I'm getting many unresolved symbol errors, and I can't figure out why.
The most common sources of this issue seem to be misspelled method names, forgetting to indicate what class the methods belong to using the scoping operator, and forgetting to include the .cpp file in the project.
I've checked all of these issues though, and none of them seem to apply. I'm telling it what class the methods belong to (via Point<T>:: ...), I've double checked the method names, and they're all correct, and the .cpp file is shown in the Solution Explorer (I even tried removing it and re-adding it).
I haven't written C++ in awhile, so I may be overlooking something, but I can't see what.
(After posting this, I realized my operators are comically broken. That shouldn't effect my error though, so please ignore for the time being).
Specifically, these are the errors I'm getting:
1>Main.obj : error LNK2019: unresolved external symbol "public: __thiscall Point<int>::Point<int>(int,int)" (??0?$Point#H##QAE#HH#Z) referenced in function _main
1>Main.obj : error LNK2019: unresolved external symbol "public: class Point<int> __thiscall Point<int>::operator+(class Point<int>)const " (??H?$Point#H##QBE?AV0#V0##Z) referenced in function _main
1>Main.obj : error LNK2019: unresolved external symbol "public: class Point<int> __thiscall Point<int>::operator-(class Point<int>)const " (??G?$Point#H##QBE?AV0#V0##Z) referenced in function _main
1>Main.obj : error LNK2019: unresolved external symbol "public: class Point<int> __thiscall Point<int>::operator*(class Point<int>)const " (??D?$Point#H##QBE?AV0#V0##Z) referenced in function _main
1>Main.obj : error LNK2019: unresolved external symbol "public: class Point<int> __thiscall Point<int>::operator/(class Point<int>)const " (??K?$Point#H##QBE?AV0#V0##Z) referenced in function _main
1>Main.obj : error LNK2019: unresolved external symbol "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class Point<int>)" (??6#YAAAV?$basic_ostream#DU?$char_traits#D#std###std##AAV01#V?$Point#H###Z) referenced in function _main
And these are the files in question:
Point.h
#ifndef POINT_H
#define POINT_H
#include <iostream>
template <typename T>
class Point {
const T x;
const T y;
public:
Point(T x, T y);
Point<T> moveBy(T xOff, T yOff) const;
Point<T> operator+(const Point<T> otherPoint) const;
Point<T> operator-(const Point<T> otherPoint) const;
Point<T> operator*(const Point<T> otherPoint) const;
Point<T> operator/(const Point<T> otherPoint) const;
friend std::ostream& operator<<(std::ostream& os, const Point<T> p);
private:
Point<T> applyOperator(T(*f)(T, T), const Point<T> otherPoint) const;
};
template <typename T>
std::ostream& operator<<(std::ostream& os, const Point<T> p) {
return os << "(" << p.x << "," << p.y << ")";
}
#endif
Point.cpp:
#include "Point.h"
template <typename T>
Point<T>::Point(T x, T y) :
x(x),
y(y) {
}
template <typename T>
Point<T> Point<T>::moveBy(T xOff, T yOff) const {
return this + Point(xOff, yOff);
}
template <typename T>
Point<T> Point<T>::operator+(const Point<T> otherPoint) const {
return applyOperator([](x, y) {x + y});
}
template <typename T>
Point<T> Point<T>::operator-(const Point<T> otherPoint) const {
return applyOperator([](x, y) {x - y});
}
template <typename T>
Point<T> Point<T>::operator*(const Point<T> otherPoint) const {
return applyOperator([](x, y) {x * y});
}
template <typename T>
Point<T> Point<T>::operator/(const Point<T> otherPoint) const {
return applyOperator([](x, y) {x / y});
}
template <typename T>
Point<T> Point<T>::applyOperator(T(*f)(T, T), const Point<T> otherPoint) const {
return Point(f(this.x, otherPoint.x), f(this.y, otherPoint.y));
}
And the Main.cpp:
#include <iostream>
#include "Point.h"
int main(int argc, char* argv[]) {
Point<int> p1(1, 2);
Point<int> p2(2, 3);
std::cout << p1 << std::endl;
std::cout << p2 << std::endl;
Point<int> p3 = p1 + p2;
Point<int> p4 = p1 - p2;
Point<int> p5 = p1 * p2;
Point<int> p6 = p1 / p2;
std::cout << p3 << std::endl;
std::cout << p4 << std::endl;
std::cout << p5 << std::endl;
std::cout << p6 << std::endl;
}
The problem is that the class template member function definitions are in Point.cpp, but when this file is compiled the compiler doesn't know that the template needs to be instantiated for T = int. In fact, I imagine Point.obj is essentially empty.
The compiler happily compiles Main.cpp with its calls to Point<int>'s members - expecting them to be provided at link time - but the linker then complains because it can't find those members defined anywhere.
The solution is to either (a) move Point's member function definitions into Point.hpp, or (b) force the instantiation of Point<int> in Point.cpp by explicitly declaring that specialisation: simply add the line template class Point<int>; at the end of Point.cpp.

Clang issue: Detecting constexpr function pointer with SFINAE

Based on the answer in Detecting constexpr with SFINAE I'm trying to use SFINAE to check if a 'constexpr' is present in my class.
The problem is that the constexpr is a function pointer:
#include <type_traits>
#include <iostream>
typedef int (*ptr_t)();
int bar() { return 9; }
struct Foo {
static constexpr ptr_t ptr = &bar;
};
namespace detail {
template <ptr_t>
struct sfinae_true : std::true_type {};
template <class T>
sfinae_true<T::ptr> check(int);
// Commented out to see why clang was not evaluating to true. This should only be
// a comment when debugging!
// template <class>
// std::false_type check(...);
} // detail::
template <class T>
struct has_constexpr_f : decltype(detail::check<T>(0)) {};
int main(int argc, char *argv[]) {
std::cout << has_constexpr_f<Foo>::value << std::endl;
return 0;
}
It seems to work fine using gcc, but clang complains:
test.cxx:23:39: error: no matching function for call to 'check'
struct has_constexpr_f : decltype(detail::check<T>(0)) {};
^~~~~~~~~~~~~~~~
test.cxx:26:22: note: in instantiation of template class 'has_constexpr_f<Foo>' requested here
std::cout << has_constexpr_f<Foo>::value << std::endl;
^
test.cxx:16:25: note: candidate template ignored: substitution failure [with T = Foo]: non-type template argument for template parameter of pointer type 'ptr_t' (aka 'int (*)()') must have its address taken
sfinae_true<T::ptr> check(int);
~ ^
1 error generated.
Q1: Can anyone suggest a way of doing this which works both for Clang and GCC?
Q2: Is this a bug in gcc, clang or is this left undefined in the c++ standard?
That's not a bug in clang, but an unfortunate restriction of arguments for non-type template parameters of pointer type (see pointer as non-type template argument). Essentially, you can only use arguments of the form &something: [temp.arg.nontype]/1 (from n3797)
[if the template-parameter is a pointer, its argument can be] a constant expression (5.19) that designates the address of a
complete object with static storage duration and external or
internal linkage or a function with external or internal linkage,
including function templates and function template-ids but excluding
non-static class members, expressed (ignoring parentheses) as &
id-expression, where the id-expression is the name of an object or
function, except that the & may be omitted if the name refers to a
function or array and shall be omitted if the corresponding
template-parameter is a reference; or [..]
[emphasis mine]
You can however, use a function pointer in a constant expression that has a non-pointer type, for example a boolean expression such as
T::ptr != nullptr
This works under clang++3.5 and g++4.8.2:
#include <type_traits>
#include <iostream>
typedef int (*ptr_t)();
int bar() { return 9; }
struct Foo0 {
static constexpr ptr_t ptr = &bar;
};
struct Foo1 {
static const ptr_t ptr;
};
ptr_t const Foo1::ptr = &bar;
struct Foo2 {
static const ptr_t ptr;
};
//ptr_t const Foo2::ptr = nullptr;
namespace detail
{
template <bool>
struct sfinae_true : std::true_type {};
template <class T>
sfinae_true<(T::ptr != nullptr)> check(int);
// the result of the comparison does not care
template <class>
std::false_type check(...);
} // detail::
template <class T>
struct has_constexpr_f : decltype(detail::check<T>(0)) {};
int main(int argc, char *argv[]) {
std::cout << std::boolalpha << has_constexpr_f<Foo0>::value << std::endl;
std::cout << std::boolalpha << has_constexpr_f<Foo1>::value << std::endl;
std::cout << std::boolalpha << has_constexpr_f<Foo2>::value << std::endl;
return 0;
}
Note there's a difference between clang++ and g++ for the second output (Foo1): g++ says true, clang++ says false.

Specializing std::hash to derived classes

I have an abstract base class Hashable that classes that can be hashed derive from. I would now like to extend std::hash to all classes that derive from Hashable.
The following code is supposed to do exactly that.
#include <functional>
#include <type_traits>
#include <iostream>
class Hashable {
public:
virtual ~Hashable() {}
virtual std::size_t Hash() const =0;
};
class Derived : public Hashable {
public:
std::size_t Hash() const {
return 0;
}
};
// Specialization of std::hash to operate on Hashable or any class derived from
// Hashable.
namespace std {
template<class C>
struct hash {
typename std::enable_if<std::is_base_of<Hashable, C>::value, std::size_t>::type
operator()(const C& object) const {
return object.Hash();
}
};
}
int main(int, char**) {
std::hash<Derived> hasher;
Derived d;
std::cout << hasher(d) << std::endl;
return 0;
}
The above code works exactly as expected with gcc 4.8.1, but when I try to compile it with gcc 4.7.2, I get the following:
$ g++ -std=c++11 -o test test_hash.cpp
test_hash.cpp:22:8: error: redefinition of ‘struct std::hash<_Tp>’
In file included from /usr/include/c++/4.7/functional:59:0,
from test_hash.cpp:1:
/usr/include/c++/4.7/bits/functional_hash.h:58:12: error: previous definition of ‘struct std::hash<_Tp>’
/usr/include/c++/4.7/bits/functional_hash.h: In instantiation of ‘struct std::hash<Derived>’:
test_hash.cpp:31:24: required from here
/usr/include/c++/4.7/bits/functional_hash.h:60:7: error: static assertion failed: std::hash is not specialized for this type
Can anybody think of a way to make this specialization of std::hash work for any class derived from Hashable with gcc 4.7.2?
It seems like there is no proper way to do what I wanted to do. I have decided to just write separate specializations for each derived class, using the following macro:
// macro to conveniently define specialization for a class derived from Hashable
#define DEFINE_STD_HASH_SPECIALIZATION(hashable) \
namespace std { \
template<> \
struct hash<hashable> { \
std::size_t operator()(const hashable& object) const { \
return object.Hash(); \
} \
}; \
}
and then
// specialization of std::hash for Derived
DEFINE_STD_HASH_SPECIALIZATION(Derived);

Resources