Check existence of operator() - c++

I need a type trait HasCall to check validity of the folowing type instantiation for T:
template<class T> struct Caller: T
{
using T::operator();
};
Is there a way to do this in C++14?
Here is my attempt, but it doesn't work:
https://godbolt.org/z/vxgJCR
EDIT
I know about SFINAE and how it works. This problem is more dificult than just checking validity of some expression.
I want these asserts to pass:
struct A {void operator()(int) {}};
static_assert(HasCall<A>, "Check must work with any set of arguments.");
struct B {void operator()() {}};
static_assert(HasCall<B>, "Check must work with any set of arguments.");
struct C {template<typename... Args> void operator()(Args&&...) {}};
static_assert(HasCall<C>, "Templated operators must be detected correctly.");
struct D {};
static_assert(!HasCall<D>, "No operator() at all.");
static_assert(!HasCall<void(*)()>, "Class cannot inherit from function pointers.");
Checking validity of expression &T::operator() is not enough because it doesn't work with overloaded or template operator().
Please, check your solutions with these asserts.
This question is not a duplicate.

No, there is no way to do this.
All solutions require knowing the signature, either exact or compatible, of the call, or rely on no overloads.
In fact any overloaded or template call operator cannot be reliably detected even if you have a known signature, as implicit cast to function pointer permits declval tests to be spoofed.
You will have to find another way around your problem, or wait for reflection.

Try this:
template <typename T, typename = void> struct has_operator {
enum { value = 0 };
};
// thanks super for great suggestion!
template <typename T> struct has_operator<T, std::void_t<decltype(std::declval<T>()())>> {
enum { value = 1 };
};
template<class T, typename = std::enable_if<has_operator<T>::value>> struct Caller: T
{
using T::operator();
};
This works by principle of SFINAE - some errors in template instantation instead of failing whole compilation will just make compiler ignore given instantation. So first we define has_operator with value = 0, which is our "default value". Then we make specialization of the template for type T. Now we want this specialization to be picked only, when T::operator() exists. So we add second template argument and set it's default value to decltype(&T::operator()) and value = 1. We don't care for real type here, what we care is that if T::operator() exists, this will compile just fine. And compiler will select this specialization for this T. When it doesn't exist - compiler will ignore this specialization and select "default" has_operator, which has value = 0.
So now we have struct has_operator, which - when used like this: has_operator<T>::value will produce constant value 0, when T don't have operator () (with any arguments, mind you) and value 1, when has. You can use it with std::enable_if (which btw works in pretty much the same way).
The list of things, that can be applied with this technique is rather long - pretty much anything, that can make or break compilation can be used.

Related

Why when use enable_if in class template have to set the second parameter's default type as void?

I'm currently studying enable_if and I have this code:
//template<typename T, typename = int/double/float/...> //not working properly
template<typename T, typename = void> //works fine
struct test{
void func(){
cout << "default" << endl;
}
};
template<typename T>
struct test<T, typename std::enable_if<(sizeof(T) <= 1)>::type>{
void func(){
cout << "called" << endl;
}
};
int main() {
test<char> objs1;
objs1.func(); //called
test<int> objs2;
objs2.func(); //default
}
I don't know the reason why I have to set the second parameter's default value as void. If I set it to other values like int or float or double, both objs1.func(); and objs2.func(); will print default. What is the reason?
So, std::enable_if<...>::type is, in fact, a type. Because you didn't specify what the type should be, you just specified the condition for which it exists at all, the default is void.
Let's look at your second version of the template. If sizeof(T) <= 1, you provide a template specialization for test<T, void>. Otherwise, the substitution fails and you provide nothing.
Now let's consider what happens when you just write test<char> objs1;. In your original version, because the default value for the unnamed second template parameter was void, this means objs1 is actually of type test<char, void>. And we actually have a specialization for test<char, void>, because sizeof(char) <= 1 is true.
However, if you change the default value of the unnamed second template parameter, we get a very different situation. Say you make the default value int instead of void. Then test<char> objs1; is actually declaring an object of type test<char, int>. We have a specialization defined for test<char, void>... But we aren't trying to create a test<char, void>, we're trying to create the separate type test<char, int>. So the fact that the condition of the enable_if is true is neither here nor there and we get the default definition of test.
The technique that is being used is SFINAE implemented via partial template specialization. In order to have multiple different types of tests depending on the characteristics of T we need to have the SFINAE expression in the template parameter list. Since class cannot be overloaded you build an "overload set" by creating a main default template and then partial specializations for all of the different cases. To do that the main template needs to have two parameters, T and the type that enable_if will resolve to. We default that second parameter to void so that it does not need to be specified by the caller to get the main template.

How is charT checked for/enforced in template code?

I am very new to c++ and am trying to understand how "generic" types are "enforced" in templates, specifically with something like charT.
After reading this question I understand that charT can be any char-like object, but I am wondering what the appropriate way is to check that the user actually supplied a valid charT.
In other words, what happens if you provide a double instead? Do you just rely on the complier throwing an error when the function inevitably tries to do something invalid with the double? Or is there a formal way to check that the supplied type is valid?
Generally, this is can be done in C++ using type traits, std::enable_if and SFINAE (Substitution Failure Is Not An Error), assuming that you're using pre-C++20 code. The basic principle is to check if a type has a certain property and if it doesn't, to disable a function overload or class specialization.
For example, if you want to write a function template that only makes sense for classes derived from a certain base class, you can write:
#include <type_traits>
struct Base {};
struct Derived : public Base {};
template<typename T, typename = std::enable_if_t<std::is_base_of_v<Base, T>>>
void foo(const T&) {
}
int main() {
Derived d{};
foo(d);
//foo(1); <-- ERROR: requirement not fulfilled
return 0;
}
Here, a second, unnamed template parameter is used and assigned to the type "returned" by std::enable_if_t (this is shorthand for typename std::enable_if<...>::type to save one from typing out all that and is available as of C++14). std::enable_if will only have the type definition called type if the first parameter evaluates to true. In this example, we check if std::is_base_of_v<Base, T> is true. If that is the case, then std::enable_if will be instantiated to have a using type = void; declaration (if one desires different type than void, this can be passed to std::enable_if as a second parameter right after the bool parameter). Therefore, the unnamed parameter will be set to void and the program compiles. If, however, the first parameter evaluates to false, there will be no type definition in std::enable_if, and the function template will not be instantiated, because the type for the second parameter in our template cannot be substituted, which will merely remove the function from the overload set and not cause a compiler error. This is what SFINAE means. Of course if no other viable overload can be found, you will still get an error, but not due to the fact that the template instantiation failed.
If you uncomment the call to foo(1), clang, for instance, will say:
<source>:15:5: error: no matching function for call to 'foo'
foo(1);
^~~
<source>:8:6: note: candidate template ignored: requirement 'std::is_base_of_v<Base, int>' was not satisfied [with T = int]
void foo(const T&) {
This, of course, is much more readable that some page-long, cryptic error message arising from an error from within the function caused by some operation that simply cannot be called on an int.
Using C++20 concepts, you could also write the template like so:
template<typename T>
requires std::is_base_of_v<Base, T>
void foo(const T&) {
}
Concepts greatly improve readability as template metaprogramming code can quickly get very lenghty and hard to read.
If you need to check more complex properties of a type, you can combine multiple type traits or write your own, which can get very complicated depending on what it is that you need to check.
I hope that clears things up a little bit for you!

Why does enable_if<>* = nullptr work when enable_if<> = void doesn't?

Basic Problem Statement
I'm learning about SFINAE. I tried an extremely simple enable_if:
// 1: A foo() that accepts arguments that are derived from Base
template <typename T, typename Enable = enable_if_t<std::is_base_of_v<Base, T>>>
void foo(T thing) {
std::cout << "It's a derived!" << std::endl;
}
// 2: A foo() that accepts all other arguments
template <typename T, typename Enable = enable_if_t<!std::is_base_of_v<Base, T>>>
void foo(T thing) {
std::cout << "It's not a derived." << std::endl;
}
The compiler complains that foo is multiply defined. The internet tells me that this is because the template arguments aren't relevant when checking function signatures.
Variants I Tried
In my quest to do the most basic metaprogramming possible, I began throwing syntax at the problem. Here's a list of things that I tried for the enable_if statement (inverted statement i.e. !std::is_base_of identical, but omitted for brevity):
Anonymous Type, No typename, Equals 0
https://en.cppreference.com/w/cpp/types/enable_if tells me that what I did above was wrong. But its suggestion (found under the first notes block) is appropriately cryptic, and more importantly, also doesn't compile.
std::enable_if_t<std::is_base_of_v<Base, T>> = 0
Anonymous Type, No typename, Equals void
Thinking that maybe if I'm programming with types, using a type would be a wise choice, I instead tried to default the template to void. No dice.
std::enable_if_t<std::is_base_of_v<Base, T>> = void
Anonymous Type, Yes typename, Equals void
While we're throwing syntax at it, if I'm defaulting this template parameter to a type, shouldn't I use the typename keyword?
typename std::enable_if_t<std::is_base_of_v<Base, T>> = void
What Finally And Oh So Obviously Worked
typename enable_if_t<std::is_base_of_v<Base, T>, T>* = nullptr
I've asked everyone I know why this works yet my other variants don't, and they are equally confused. I'm at a loss. To make matters more confusing, if I name this type (e.g. typename Enable = ...), it fails to compile.
I would be extremely grateful if one who is more familiar with TMP and enable_if would explain to me:
Why does declaring the enable_if as a pointer to a type and defaulting it to nullptr work?
What are the semantic rules for defaulting enable_if?
What are the semantic rules for naming types produced by enable_if?
Is there a reference I can use which clearly explains this and other rules like it in template-land?
Many thanks.
The first set of variants you are just setting the value of a template type argument. Two overloads with different values for a template type argument collide, as they are both of kind template<class,class> and have the same function arguments.
The non-type template argument cases, the ones where you use a raw enable if you end up having a template non type argument of type void. That is illegal; the various error messages are the various ways it is illegal.
When you add a star, when the enable if clause passes it is a template non type argument of type void pointer.
When it fails, it isn't an argument at all.
An equivalent to the nullptr case is:
std::enable_if_t<std::is_base_of_v<Base, T>, bool> = true
when the clause is true, the enable if evaluates to bool, and we get:
bool = true
a template non-type argument of type bool that defaults to true. When the clause (the base of clause) is false, we get a SFINAE failure; there is no template type or non-type argument there.
With the class Whatever = enable_if cases we are trying SFINAE based on default value of template arguments. This leads to signature collision, because signatures have to be unique if they are found during overload resolution (in the same phase).
With the enable = value cases, we are trying SFINAE based on if there is a template non-type argument there. On failure, there is no signature to compare, so it cannot collide.
What remains is to make the syntax simple and pretty.
Now, this is all obsolete with Concepts, so don't fall in love with the syntax.

A safe, standard-compliant way to make a class template specialization fail to compile using `static_assert` only if it is instantiated?

Assume that we want to make a template class that can only be instantiated with numbers and should not compile otherwise. My attempt:
#include <type_traits>
template<typename T, typename = void>
struct OnlyNumbers{
public:
struct C{};
static_assert(std::is_same<C,T>::value, "T is not arithmetic type.");
//OnlyNumbers<C>* ptr;
};
template<typename T>
struct OnlyNumbers<T, std::enable_if_t<std::is_arithmetic_v<T>>>{};
struct Foo{};
int main()
{
OnlyNumbers<int>{}; //Compiles
//OnlyNumbers<Foo>{}; //Error
}
Live demo - All three major compilers seem to work as expected. I'm aware that there is already a similar question with answers quoting the standard. The accepted answer uses temp.res.8 together with temp.dep.1 to answer that question. I think my question is a bit different because I'm asking precisely about my example and I'm not sure about the standard's opinion on it.
I would argue that my program is not ill-formed and that it should fail to compile if and only if the compiler tries to instantiate the base template.
My reasoning:
[temp.dep.1]:
Inside a template, some constructs have semantics which may differ from one instantiation to another. Such a construct depends on the template parameters.
This should make std::is_same<C,T>::value dependent on T.
[temp.res.8.1]:
no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated, or
Does not apply because there exist a valid specialization, in particular OnlyNumbers<C> is valid and can be used inside the class for e.g. defining a member pointer variable(ptr). Indeed by removing the assert and uncommenting the ptr, OnlyNumbers<Foo> lines the code compiles.
[temp.res.8.2 - 8.4] does not apply.
[temp.res.8.5] I don't think this applies either but I cannot say that I fully understand this section.
My question is: Is my reasoning correct? Is this a safe, standard-compliant way to make a particular [class]* template fail to compile using static_assert if** and only if it is instantiated?
*Primarily I'm interested in class templates, feel free to include function templates. But I think the rules are the same.
**That means that there is no T which can be used to instantiate the template from the outside like T=C could be used from the inside. Even if C could be accessed somehow I don't think there's a way to refer to it because it leads to this recursion OnlyNumbers<OnlyNumbers<...>::C>.
EDIT:
Just to clarify, I know that I can construct an expression that will be false exactly if none of the other specializations match. But that can become wordy quite quickly and is error-prone if specializations change.
Static assertions are there to be used directly in the class without doing anything complicated.
#include <type_traits>
template<typename T>
struct OnlyNumbers {
static_assert(std::is_arithmetic_v<T>, "T is not arithmetic type.");
// ....
};
In some cases, you might get additional error messages since instanciating OnlyNumbers for non-arithmetic types might cause more compilation errors.
One trick I have used from time to time is
#include <type_traits>
template<typename T>
struct OnlyNumbers {
static_assert(std::is_arithmetic_v<T>, "T is not arithmetic type.");
using TT = std::conditional_t<std::is_arithmetic_v<T>,T,int>;
// ....
};
In this case, your class gets instanciated with int, a valid type. Since the static assertion fails anyway, this does not have negative effects.
Well... I don't understand what do you mean with
[[temp.res.8.1]] Does not apply because there exist a valid specialization, in particular OnlyNumbers is valid and can be used inside the class for e.g. defining a member pointer variable(ptr).
Can you give an example of OnlyNumers valid and compiling main template based on OnlyNumbers<C>?
Anyway, it seems to me that the point is exactly this.
If you ask
Is this a safe, standard-compliant way to make a particular [class]* template fail to compile using static_assert if** and only if it is instantiated?
it seems to me that (maybe excluding a test that is true only when another specialization matches) the answer is "no" because of [temp.res.8.1].
Maybe you could let a little open door open to permit an instantiation but available only is someone really (really!) want instantiate it.
By example, you could add a third template parameter, with different default value, and something as follows
template<typename T, typename U = void, typename V = int>
struct OnlyNumbers
{
static_assert(std::is_same<T, U>::value, "test 1");
static_assert(std::is_same<T, V>::value, "test 2");
};
This way you open a door to a legit instantiation
OnlyNumbers<Foo, Foo, Foo> o1;
OnlyNumbers<void, void, void> o2;
OnlyNumbers<int, int> o3;
but only explicating at least a second template type.
Anyway, why don't you simply avoid to define the main version of the template?
// declared but (main version) not defined
template<typename T, typename = void>
struct OnlyNumbers;
// only specialization defined
template<typename T>
struct OnlyNumbers<T, std::enable_if_t<std::is_arithmetic_v<T>>>
{ };
Your code is ill-formed since the primary template cannot be instantiated. See the standard quote in Barry's answer to the related question you linked to. The roundabout way you have used to ensure that the clearly stated standard requirement cannot be met, does not help. Stop fighting your compiler rsp. the standard, and go with Handy999's approach. If you still don't want to do that e.g. for DRY reasons, then a conformant way to achieve your goal would be:
template<typename T, typename Dummy = void>
struct OnlyNumbers{
public:
struct C{};
static_assert(! std::is_same<Dummy, void>::value, "T is not a number type.");
Two remarks:
First, I deliberately replaced the error message because the error message "is not an arithmetic type" screams that you must test ! std::is_arithmetic<T>::value. The approach I've outlined potentially makes sense if you have multiple overloads for "numeric" types, some of which meet the standard's arithmetic type requirements and others might not (e.g. maybe a type from a multiprecision library).
Second, you might object that someone could write e.g. OnlyNumbers<std::string, int> to defeat the static assertion. To which I say, that's their problem. Remember that every time you make something idiot proof, nature makes a better idiot. ;-) Seriously, do make APIs that are easy to use and hard to abuse, but you cannot fix insanity and shouldn't bother trying.
TL;DR: KISS and SWYM (say what you mean)

Partial specialization and the need for std::void_t<>

One for the language lawyers....
I'm playing around with SFINAE and TMP, trying to get a deeper understanding.
Consider the following code, a naive implementation of std::is_default_constructible
#include <type_traits>
template <typename T, typename = void> struct is_default_constructable : std::false_type {};
template <typename T> struct is_default_constructable<T, decltype(T()) > : std::true_type {};
class NC { NC(int); }; // Not default constructable
#include <iostream>
int main(int, char **)
{
std::cout << "int is_default_constructible? " << is_default_constructable<int>() << std::endl;
std::cout << "NC is_default_constructible? " << is_default_constructable<NC>() << std::endl;
}
This compiles fine, but doesn't actually work, it returns false for all types.
For the NC case, this is as I'd expect, T() is not well-formed so that specialization is discarded due to SFINAE and the primary template (false_type) is used. But for the int case, I'd expect the specialization to be used as decltype(T()) is valid and equivalent to T.
If, based on the actual code in <type_traits>, I change the specialization to
template <typename T> using wrap = void;
template <typename T> struct is_default_constructable<T, wrap<decltype(T())> > : std::true_type {};
(i.e. wrap the second template parameter in a mockup of std::void_t<> which forces the second type to be void), this works as expected.
Even more curious, variations of this scheme using types other than void as the default type in the primary template or wrap<> also fail, unless the two types are the same.
Can someone explain why the type of wrap<> and the second template argument default type need to be the same in order for the specialization to be selected?
(I'm using "g++ -Wall --std=c++17" with g++ version 6.3, but I think this is not compiler-related.)
This is not a consequence of SFINAE or partial specialization ordering, but due to the use of default template parameters. Informally, the reason is that the application of default template parameters happens before the search for template definitions, including possible specializations.
So in the above case, the code that says is_default_constructable<int> is actually requesting to instantiate a template is_default_constructable<int, void> after applying the default second parameter. Then possible definitions are considered.
The "primary" template definition clearly matches and is included.
The given partial specialization
template <typename T> struct is_default_constructable<T, decltype(T()) > : std::true_type {};
is actually defining is_default_constructable<int, int> which does not match the requested is_default_constructable<int, void> so the specialization is ignored, even if the substitution succeeds.
This leaves the primary definition (inheriting false_type) as the only viable definition so it is chosen.
When the specialization has wrap<> (or std::void_t<>) to force the second arg to a void, the specialization is defining is_default_constructable<int, void> which matches the request. This definition (asuming the substitution succeeds, i.e. T() is well formed) is more specialized than the primary definition (according to super-complicated rules for ordering specializations), so it is chosen.
As an aside, the above naive implementations probably don't work as expected when T is a reference type or other corner cases, which is a good reason to use the standard library versions of all this. Them standards committee people are way smarter than I am and have already thought of all these things.
This answer and this answer to somewhat-related questions have more detailed information that set me right.
And, yes, I can't spell constructible, assuming that's even a word. Which is another good reason to use the standard library.