This question covers C++03, and how to omit a conversion operator from a class template, say template<typename T> struct Foo { ... };
, given traits on T
.
(Questions at the bottom)
Background
Ideally, I'd would like to make use of an enable_if
construct and SFINAE to exclude a conversion operator based on traits of T
of class template (see Foo
above), but default template arguments may not be used in function templates in C++03, which (afaik) excludes that approach; as previously covered in the following thread:
Instead, I'm using an approach where the type of the return value of the conversion operator is conditional on traits on T
, particularly being a dummy (void
or some private externally inaccessible type) for certain types of T
. This approach seems to work fine, but I'm uncertain what possible pitfalls I might be digging for myself; specifically what is guaranteed by the (C++03) Standard in this context.
Consider the following example (which I've tried to keep as minimal as possible), using the approach described in the previous paragraph:
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;
}
This compiles fine using gcc
and clang
(-std=c++03
), but I'm wondering if it's really OK to conditionally have an invalid body(/return) for the conversion operator, as is the case e.g. for a full instantiation of Foo<char>
. I'm assuming it's fine due to partial implicit instantiation; [temp.inst]/1 (14.7.1 in the C++03 standard draft) describes [emphasis mine]:
The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions or default arguments, of the class member functions, member classes, static data members and member templates; and it causes the implicit instantiation of the definitions of member anonymous unions. Unless a member of a class template or a member template has been explicitly instantiated or explicitly specialized, the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist;
Question
- Does the C++03 Standard guarantee that a conditionally (trait on
T
) invalid non-template member function body of a class template is not an error in case it is not referenced from instantiations of the class where it is/would be invalid?