这个问题涵盖了C++03,以及如何从类模板中省略一个转换操作符,比如template<typename T> struct Foo { ... };
,给定T
的traits。
- (底部的问题)*
背景
理想情况下,我希望使用enable_if
构造和SFINAE来排除基于类模板T
特性的转换操作符(参见上面的Foo
),但默认模板参数不能用于C++03中的函数模板,这(afaik)排除了这种方法;如之前在以下线程中所述:
相反,我使用的方法是转换操作符返回值的类型取决于T
的特性,特别是对于某些T
类型,它是一个伪类型(void
或一些私有的外部不可访问类型)。特别是(C++03)标准在此上下文中所保证的内容。
考虑下面的例子(我已经尽可能地减少了这个例子),使用上一段描述的方法:
include/util.h
:
namespace util {
// dummy predicate: is T int?
template <typename T> struct is_int { static const bool value = false; };
template <> struct is_int<int> { static const bool value = true; };
template <typename T> const bool is_int<T>::value;
// [meta.trans.other]/conditional
template <bool B, class T, class F> struct conditional { typedef T type; };
template <class T, class F> struct conditional<false, T, F> { typedef F type; };
// class template with non-template operator() member
template <typename T> struct Foo {
explicit Foo(const T &value) : value_(value) {}
// [Question regarding this conversion operator here]
operator typename conditional<is_int<T>::value, int, void>::type() const {
return value_;
}
private:
T value_;
};
/* Alternatively */
template <typename T> class Bar {
struct Dummy {};
T value_;
public:
explicit Bar(const T &value) : value_(value) {}
operator typename conditional<is_int<T>::value, int, Dummy>::type() const {
return value_;
}
};
} // namespace util
main.cc
:
#include "include/util.h"
void baz(int) {}
int main()
{
const util::Foo<int> foo_int(42);
baz(foo_int); // OK
const util::Foo<char> foo_char('a');
const util::Bar<int> bar_int(42);
baz(bar_int); // OK
const util::Bar<char> bar_char('a');
/* OK, expected:
Error: cannot convert ‘const util::Foo<char>’/‘const util::Bar<char>’
to ‘int’ for argument ‘1’ to ‘void baz(int)
baz(foo_char);
baz(bar_char); */
return 0;
}
使用gcc
和clang
(-std=c++03
)可以很好地编译,但是我想知道有条件地为转换操作符使用一个无效的主体(/return)是否真的可以,例如Foo<char>
的完整示例化。[温度 Jmeter ]/1(14.7.1 in the C++03 standard draft)描述了[强调我的]:
类模板专门化的隐式示例化导致类成员函数**、成员类、静态数据成员和成员模板的声明的隐式示例化,而不是定义或缺省参数的隐式示例化;并且它导致成员匿名联合的定义的隐式示例化。除非类模板的成员 * 或成员模板已经被显式示例化或显式专门化,否则当在要求成员定义存在的上下文中引用该专门化时**,该成员的专门化被隐式示例化 *;
问题
- C++03标准是否保证类模板的条件(
T
上的trait)无效的非模板成员函数体不是错误,如果它不是从类的示例化引用的,在那里它是/将是无效的?
1条答案
按热度按时间esyap4oy1#
你的推理是正确的。
根据所引用的段落([temp.inst]/1),当
Foo<T>
对于某些Foo
被 * 隐式地 * 示例化时,这并不示例化成员函数的定义,并且成员函数定义直到需要该定义存在时才被示例化。定义必须存在于一个定义规则下,具体为[basic.def.odr]/3下:
每个程序应包含该程序中使用的每个非内联函数或对象的一个定义;不需要诊断。定义可以显式出现在程序中,也可以在标准库或用户定义库中找到,或者(适当时)隐式定义(见12.1、12.4和12.8)。内联函数应在使用它的每个翻译单元中定义。
[basic.def.odr]/2解释了“使用”哪些函数:
[...]如果一个对象或非重载函数的名称出现在一个可能求值的表达式中,则该对象或非重载函数是 used。如果它不是pure,则使用虚成员函数。如果从一个可能求值的表达式引用时,重载函数被重载解析选择,则使用重载函数。[* 注意:* 这涵盖了对命名函数的调用(5.2.2)、运算符重载(第13条)、用户定义的转换(12.3.2)、用于放置new的分配函数(5.3.4)以及非默认初始化(8.5)。即使实现实际上省略了调用,也使用复制构造函数。] [...]
(NB:在C++11中,这个概念被重新命名为“odr-use”。
当你仅仅创建一个
util::Foo<char>
或util::Bar<char>
类型的对象时,并不需要使用该类可能有的任何转换函数,因此在One Definition规则下不需要定义。最后,[temp.inst]/9承诺:一个实现不应该隐式示例化一个函数模板、一个成员模板、一个非虚成员函数、一个成员类或者一个不需要示例化的类模板的静态数据成员。
所以你是安全的:则不示例化格式不正确的主体。
另一种有条件声明转换函数的方法是从一个CRTP基类继承,这个基类可能有也可能没有转换函数,看起来像这样(我还没有编译它,所以它可能包含错误):