I have a class, let's call it Foo
with several methods:
template<typename T>
class Foo {
public:
Foo() { /* ... */ }
bool do_something() { /* ... */ }
// This method should be callable only if:
// std::is_floating_point<T>::value == true
void bar() {
// Do stuff that is impossible with integer
}
};
I would like to be able to construct both Foo<double>
and Foo<int>
But I don't want to allow calls to bar()
when the type T is not a floating point type. I also want the error to be generated at compile-time and not at the run-time. So, what I want is:
Foo<double> a;
a.bar(); // OK
Foo<int> b;
bool res = b.do_something(); // OK
b.bar(); // WRONG: compile error
I tried a lot of things with enable_if
(with posts like this or this one) but I can't use anymore the int
type with Foo
. For example:
typename std::enable_if<std::is_floating_point<T>::value>::type
bar() { /* ... */ }
main.cpp:112:28: required from here
foo.h:336:5: error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
bar() {
How can I constrain the use of bar()
to floating point types but allow integer type to use in other places?
The basic solution is to use a
static_assert
within the body ofbar
. This generates an error if and only if someone tries to call it. It does not, however, block people from finding it, and the error is "late" in that it doesn't enable SFINAE detection of the existence ofbar
.As an alternative to
static_assert
, you can use SFINAE to conditionally allowbar
to participate in overload resolution, and possibly block it from being found to be called at all. You have to be careful, because the SFINAE must theoretically have a specialization that compiles (otherwise your program is ill-formed with no diagnosis required), and it only works on atemplate
method.This blocks the method
bar
from being considered during overload resolution, rather than making selecting it an error. This is a subtle difference, but matters a lot ifbar
is overridden.which is pretty obtuse. In C++1y with concepts lite,
requires
clauses are supposed to do the right thing within atemplate
without the above obtuse stuff.In practice, the
blocked
type is probably not needed, as I am not aware of a compiler that actually enforces the ill-formedness of atemplate
that has no valid specializations. That clause is probably there to allow future revisions to do more checking prior to instantiation.The final method I can think of involves a CRTP base class that specializes based on the trait you want to create/destroy
bar
based on.then at the point of use:
and
bar
is conditionally created or not.This is less obtuse, but more verbose, and it places the implementation seriously out-of-line with the rest of the class body.