main.cpp: In function ‘void printContainer(const T&)’: main.cpp:20:11: error: need ‘typename’ before ‘T::const_iterator’ because ‘T’ is a dependent scope const T::const_iterator it, end(container.cend());
public: // constructor for passed initial name: explicitPerson(std::string const& n) : name(n) { std::cout << "copying string-CONSTR for '" << name << "'\n"; } explicitPerson(std::string&& n) : name(std::move(n)) { std::cout << "moving string-CONSTR for '" << name << "'\n"; } // copy and move constructor: Person(Person const& p) : name(p.name) { std::cout << "COPY-CONSTR Person '" << name << "'\n"; } Person(Person&& p) : name(std::move(p.name)) { std::cout << "MOVE-CONSTR Person '" << name << "'\n"; } };
intmain(){ std::string s = "sname"; Person p1(s); // init with string object => calls copying string-CONSTR Person p2("tmp"); // init with string literal => calls moving string-CONSTR Person p3(p1); // copy Person => calls COPY-CONSTR Person p4(std::move(p1)); // move Person => calls MOVE-CONST }
利用上面的转发机制,可以将构造函数写成模板函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
classPerson { private: std::string name;
public: // generic constructor for passed initial name: template <typename STR> explicitPerson(STR&& n) : name(std::forward<STR>(n)) { std::cout << "TMPL-CONSTR for '" << name << "'\n"; }
// copy and move constructor: Person(Person const& p) : name(p.name) { std::cout << "COPY-CONSTR Person '" << name << "'\n"; } Person(Person&& p) : name(std::move(p.name)) { std::cout << "MOVE-CONSTR Person '" << name << "'\n"; } };
但是这么做会有问题,在编译的时候,下面的语句会报错:
1
Person p3(p1); // copy Person => calls COPY-CONSTR
// use of the template intmain() { double ice = 3.0; printTypeof(ice); // call function template for type double }
当我们试着编译这段程序的时候,会报连接错误:
1 2 3 4
> g++ myfirstmain.cpp myfirst.cpp -o myfirst /tmp/cc3q27zY.o: In function `main': myfirstmain.cpp:(.text+0x2c): undefined reference to `void printTypeof<double>(double const&)' collect2: error: ld returned 1 exit status
std::remove_const_t<intconst&> // -> int const& std::remove_const_t<std::remove_reference_t<intconst&>> // -> int std::remove_reference_t<std::remove_const_t<intconst&>> // -> int const
当然还可以:
1
std::decay_t<intconst&> // -> int
特性(Traits)
假设有一个求和函数:
1 2 3 4 5 6 7 8 9 10
template<typename T> T accum(T const* beg, T const* end) { T total{}; // assume this actually creates a zero value while (beg != end) { total += *beg; ++beg; } return total; }
这段模板代码在如下调用的时候会发生溢出错误:
1 2 3 4 5 6 7 8
char name[] = "templates"; int length = sizeof(name)-1;
// (try to) print average character value std::cout << "the average value of the characters in \"" << name << "\" is " << accum(name, name+length) / length << '\n';
原因是,模板中的类型 T 被实例化成为 char,所以发生了溢出错误,当然我们可以通过额外声明求和变量的类型来解决问题:
1
accum<int>(name, name+length)
类型特性(Type Traits)
通过类型特性,可以更好地解决这个问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
template<typename T> structAccumulationTraits;
template<> structAccumulationTraits<char> { using AccT = int; };
template<> structAccumulationTraits<short> { using AccT = int; };
// ...
这里的技巧是通过模板特化来选择我们需要的类型,然后求和函数代码只需要改造成下面这样既可:
1 2 3 4 5 6 7 8 9 10 11 12 13
template<typename T> autoaccum(T const* beg, T const* end) { // return type is traits of the element type using AccT = typename AccumulationTraits<T>::AccT;
AccT total{}; // assume this actually creates a zero value while (beg != end) { total += *beg; ++beg; } return total; }
值特性(Value Traits)
在原本的代码中,AccT 是根据一个 T 推断出来的类型,并且使用了花括号进行默认构造,但是 AccT 很可能没有默认构造函数,或者不能提供一个足够好的默认值,这时候可以用值特性来提供默认值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
template<typename T> structAccumulationTraits;
template<> structAccumulationTraits<char> { using AccT = int; static AccT const zero = 0; };
template<> structAccumulationTraits<short> { using AccT = int; static AccT const zero = 0; };
模板代码改造成下面这样:
1 2 3 4 5 6 7 8 9 10 11 12 13
template<typename T> autoaccum(T const* beg, T const* end) { // return type is traits of the element type using AccT = typename AccumulationTraits<T>::AccT;
AccT total = AccumulationTraits<T>::zero; // init total by trait value while (beg != end) { total += *beg; ++beg; } return total; }
template<bool val> structBoolConstant { using Type = BoolConstant<val>; staticconstexprbool value = val; }; using TrueType = BoolConstant<true>; using FalseType = BoolConstant<false>;
template<typename T> structIsDefaultConstructibleHelper { private: // test() trying substitute call of a default constructor for T passed as U: template<typename U, typename= decltype(U())> static std::true_type test(void*); // test() fallback: template<typename> static std::false_type test(...); public: using Type = decltype(test<T>(nullptr)); };