What is the moral equivalent to the following invalid code?
// Suppose you want to check, as part of a concept,
// if some operation on a type results in a type that models such concept.
// you cannot constrain the resulting type with the same concept
// in the same way you can't have self referential types
// (that would result in recursive definitions)
template<class T>
concept Int = requires(const T& a, const T& b) {
{ a + b } -> Int; // compiler don't know what to do
};
Suppose you want to check, as part of a concept, if some operation on a type results in a type that models such concept.
That's infinite recursion. Like any functional recursion, you have to have a terminal condition. The normal way to define a terminal condition for template arguments is via a specialization. But concepts explicitly cannot be specialized, so there can be no terminal condition.
It's also logically incoherent, since you're trying to write a definition by using the thing you're trying to define. There is no "moral equivalent" to something that by definition doesn't make sense.
Your concept appears to be saying "T shall be a thing that I can add to another T and yield..." what? Do you want it to be able to yield some unrelated type U which can be added to another U to yield... again, what? Even ignoring that question, should U be able to be added to T? And if so, what should that yield?
When writing a concept, start with the use case, start by deciding what operations you want to perform.
It is possible to do such recursive template check, but it makes code difficult to read.
The principle is to forward recursive template check to a function found by dependent name look up, whose constraints will only be verified if the type does not already belong to a list of already checked types... If the type belong to the list of already checked type, the function is disabled by SFINAE, and an other function that does not recursively refers to the concept is selected by overload resolution:
See it in action: compiler-explorer-link
#include <type_traits>
namespace trying{
struct to_do{};
template <class...Checked, class T>
std::enable_if_t <(std::is_same_v <T,Checked> || ...), std::true_type>
too_complex(T &&, to_do);
template <class...Checked, class T>
std::false_type
too_complex(T &&,...);
}
template <class U, class T, class...Checked>
concept Integer_= requires(const T& a, const T& b, const U& to_be_readable)
{
requires decltype(too_complex <T, Checked...> (a + b, to_be_readable))::value ;
};
template <class T, class...Checked>
concept Integer = Integer_ <trying::to_do, T, Checked...>;
namespace trying{
template <class...Checked, class T>
requires (Integer <T, Checked...>)
std::enable_if_t <!(std::is_same_v <T,Checked> || ...), std::true_type>
too_complex(T &&, to_do);
}
struct x{
auto
operator + (x) const -> int;
};
struct y{
auto
operator + (y) const -> void*;
};
struct z2;
struct z1{
auto
operator + (z1) const -> z2;
};
struct z2{
auto
operator + (z2) const -> z1;
};
static_assert (Integer <int>);
static_assert (Integer <x>);
static_assert (!Integer <y>);
static_assert (Integer <z1>);
static_assert (Integer <z2>);
So yes it is possible... but I don't think it should be done.
Related
template<typename T, size_t M, size_t K, size_t N, typename std::enable_if_t<std::is_floating_point<T>::value, T> = 0>
void fastor2d(){//...}
I copied this line of code from cpp-reference(only the std::enable_if part, i do need T and all three of the size_t's), because i would like to use this function only when floating_types are used on it ... it does not compile.
Could somebody explain to me, why, and what it even does? While i am at it, how do you call this function afterwards?
Every tutorial or question here on SO gets bombed with answers, and that is great, but to someone who does not understand jacks*** of what is happening, even those are not really helpful.(sry, if possibly slightly agitated or aggressive)
EDIT: i greatly appreciate all answers as of now, i realize that my wording might have been a bit off ... i understand what a template parameter is, and know the difference between runtime and compiletime etc, but i just cant get a good grasp of the syntax behind std::enable_if
EDIT2:
template<typename T, size_t M, size_t K, size_t N, typename = std::enable_if_t<std::is_integral<T>::value>>
void fastor2d(){
Fastor::Tensor<T,M,K> A; A.randInt();
}
This is literally the only thing i need changed. Notice the random() part
template<typename T, size_t M, size_t K, size_t N, typename = std::enable_if_t<std::is_floating_point<T>::value>>
void fastor2d(){
Fastor::Tensor<T,M,K> A; A.random();
}
I'll try to explain this as simple as possible not to go into the language details too much since you asked for it.
Template arguments are compile time arguments (they do not change during the run-time of your application). Function arguments are run-time and have a memory address.
Calling this function would look something like this:
fastor2d<Object, 1, 2, 3>();
In the <> brackets you see the compile-time arguments or more accurately the template parameters, and the function in this case takes 0 runtime arguments in the () brackets. The last compile time argument has a default argument which is used to check whether the function should compile at all (enable_if type). If you want to know more clearly what enable if does you should search for the term SFINAE, which is a template metaprogramming technique used to determine whether a function or class should exist or not.
Here is a short SFINAE example:
template<typename T, typename = std::enable_if_t<std::is_floating_point<T>::value>>
void function(T arg)
{
}
function(0.3f); //OK
function(0.0); //OK double results in std::is_floating_point<double>::value == true
function("Hello"); //Does not exist (T is not floating point)
The reason the third function call fails, is because the function does not exist. This is because the enable if caused the function not to exist when the compile-time bool that is passed in as its' template argument is false.
std::is_floating_point<std::string>::value == false
Do note that a lot of people agree that the SFINAE syntax is horrible and that a lot of SFINAE code will not be necessary anymore with the introduction of concepts and constraints in C++ 20.
Rather than a top-down approach starting with you code snippet, I'll take a bottom-up approach to explain some important details about templates and what tools and techniques are involved.
At heart, templates are a tool that let you write C++ code that applies to a range of possible types, not strictly for a fixed type. In a statically-typed language, this is firstly a great tool for reusing code without sacrificing type safety, but in C++ in particular, templates are very powerful because they can be specialized.
Every template declaration begins with the keyword template, and a list of type or non-type (i.e value) parameters. Type parameters use the special keyword typename or class, and are used to let your code work over a range of types. Non-type parameters simply use the name of an existing type, and these let you apply your code to a range of values that are known at compile-time.
A very basic templated function might look like the following:
template<typename T> // declare a template accepting a single type T
void print(T t){ // print accepts a T and returns void
std::cout << t; // we can't know what this means until the point where T is known
}
This lets us reuse code safely for a range of possible types, and we can use it as follows:
int i = 3;
double d = 3.14159;
std::string s = "Hello, world!";
print<int>(i);
print<double>(d);
print<std::string>(s);
The compiler is even smart enough to deduce the template parameter T for each of these, so you can safely get away with the following, functionally identical code:
print(i);
print(d);
print(s);
But suppose you want print to behave differently for one type. Suppose, for example, you have a custom Point2D class that needs special handling. You can do this with a template specialization:
template<> // this begins a (full) template specialization
void print<Point2D>(Point2D p){ // we are specializing the existing template print with T=Point2D
std::cout << '(' << p.x << ',' << p.y << ')';
}
Now, anytime we use print with T=Point2D, the specialization is chosen. This is really useful, for example, if the generic template just doesn't make sense for one specific type.
std::string s = "hello";
Point2D p {0.5, 2.7};
print(s); // > hello
print(p); // > (0.5,2.7)
But what if we want to specialize a template for many types at once, based on a simple condition? This is where things become a little meta. First, let's try to express a condition in a way that lets them be used inside templates. This can be a little tricky because we need compile-time answers.
The condition here will be that T is a floating point number, which is true if T=float or T=double and false otherwise. This is actually fairly simple to achieve with template specialization alone.
// the default implementation of is_floating_point<T> has a static member that is always false
template<typename T>
struct is_floating_point {
static constexpr bool value = false;
};
// the specialization is_floating_point<float> has a static member that is always true
template<>
struct is_floating_point<float> {
static constexpr bool value = true;
};
// the specialization is_floating_point<double> has a static member that is always true
template<>
struct is_floating_point<double> {
static constexpr bool value = true;
}
Now, we can query any type to see if it's a floating point number:
is_floating_point<std::string>::value == false;
is_floating_point<int>::value == false;
is_floating_point<float>::value == true;
is_floating_point<double>::value == true;
But how can we use this compile-time condition inside another template? How can we tell the compiler which template to choose when there are many possible template specializations to choose from?
This is achieved by taking advantage of a C++ rule called SFINAE, which in basic English, says, "when there are many possible template specializations to choose from, and the current one doesn't make sense*, just skip it and try the next one."
There's a list of errors, when attempting to substitute template arguments into templated code, that cause the template to be ignored without an immediate compiler error. The list is a bit long and complex.
One possible way that a template doesn't make sense is if it tries to use a type that doesn't exist.
template<typename T>
void foo(typename T::nested_type x); // SFINAE error if T does not contain nested_type
This is the exact same trick that std::enable_if uses under the hood. enable_if is a template class accepting a type T and a bool condition, and it contains a nested type type equal to T only when the condition is true. This is also pretty easy to achieve:
template<bool condition, typename T>
struct enable_if {
// no nested type!
};
template<typename T> // partial specialization for condition=true but any T
struct enable_if<true, T> {
typedef T type; // only exists when condition=true
};
Now we have a helper that we can use in place of any type. If the condition we pass is true, then we can safely use the nested type. If the condition we pass is false, then the template is no longer considered.
template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, void>::type // This is the return type!
numberFunction(T t){
std::cout << "T is a floating point";
}
template<typename T>
typename std::enable_if<!std::is_floating_point<T>::value, void>::type
numberFunction(T t){
std::cout << "T is not a floating point";
}
I completely agree that std::enable_if<std::is_floating_point<T>::value, void>::type is a messy way to spell out a type. You can read it as "void if T is floating point, and otherwise stop and try the next overload"
Finally, to take apart your example:
// we are declaring a template
template<
typename T, // that accepts some type T,
size_t M, // a size_t M,
size_t K, // a size_t K,
size_t N, // a size_t N,
// and an unnamed non-type that only makes sense when T is a floating point
typename std::enable_if_t<std::is_floating_point<T>::value, T> = 0
>
void fastor2d(){//...}
Note the = 0 at the end. That's simply a default value for the final template parameter, and it lets you get away with specifying T, M, K, and N but not the fifth parameter. The enable_if used here means that you can provide other templates called fastor2d, with their own sets of conditions.
First of all, I'll rewrite your function in a working form
template <typename T, size_t M, size_t K, size_t N,
std::enable_if_t<std::is_floating_point<T>::value, int> = 0>
void fastor2d() // ..........................................^^^ int, not T
{ }
The point is that I've changed the second template argument of std::enable_if_t form T to int.
I've also removed the typename before std::enable_if_t but isn't important: the typename is implicit in the _t at the end of std::enable_if_t, introduced from C++14. In C++11 the correct form is
// C++11 version
typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0
// ^^^^^^^^ no _t ^^^^^^
But why it works?
Start from the name: SFINAE.
Is a short form for "Substitution Failure Is Not An Error".
It's a C++ rule so that when you write some thing as
template <int I, std::enable_if_t< I == 3, int> = 0>
void foo ()
{ }
and I is 3, the condition of std::enable_if_t is true so std::enable_if_t< I == 3, int> is substituted with int so foo() is enabled but when I isn't 3, the condition of std::enable_if_t if false so std::enable_if_t< I == 3, int> is not substituted so foo() isn't enabled but this ins't an error (if, through overloading, there is another foo() function, enabled, that matches the call, obviously).
So where is the problem in your code?
The problem is that std::enable_if_t is substituted, when the first template parameter is true, with the second parameter.
So if you write
std::enable_if_t<std::is_floating_point<T>::value, T> = 0
and you call
fastor2d<float, 0u, 1u, 2u>();
the std::is_floating_point<float>::value (but you can also use the shorter form std::is_floating_point_v<T> (_v and not ::value)) so the substitution take place and you get
float = 0
but, unfortunately, a template value (not type) parameter can't be of type floating point, so you get an error.
If you use int instead of T, the substitution give you
int = 0
and this is correct.
Another solution can be use the following form
typename = std::enable_if_t<std::is_floating_point<T>::value, T>
as suggested by Andreas Loanjoe, because the substitution give you
typename = float
that is a valid syntax.
But this solution has the drawback that doesn't works when you want to write two alternative functions, as in the following example
// the following solution doesn't works
template <typename T,
typename = std::enable_if_t<true == std::is_floating_point<T>::value, int>>
void foo ()
{ }
template <typename T,
typename = std::enable_if_t<false == std::is_floating_point<T>::value, int>>
void foo ()
{ }
where works the solution based on the value
// the following works
template <typename T,
std::enable_if_t<true == std::is_floating_point<T>::value, int> = 0>
void foo ()
{ }
template <typename T,
std::enable_if_t<false == std::is_floating_point<T>::value, int> = 0>
void foo ()
{ }
I am designing an utility header that pumps binary data off an sf::InputStream. For ease of use, is comprises a single function name, readFromStream, that has a lot of (templated and non-templated) overloads for automatically deserializing standard-layout types and type compounds like vectors, tuples and my custom-designed grid class. The complete implementation can be found here: https://github.com/JoaoBaptMG/ReboundTheGame/blob/master/MainGame/utility/streamCommons.hpp
So, I have defined an overload readFromStream that pumps out a vector of any type by calling readFromStream again recursively:
template <typename T, typename std::enable_if<!is_optimization_viable<T>::value, int>::type = 0>
bool readFromStream(sf::InputStream &stream, std::vector<T> &value)
{
size_t size;
if (!readFromStream(stream, VarLength(size)))
return false;
std::vector<T> newVal(size, T());
for (auto &val : newVal)
if (!readFromStream(stream, val))
return false;
newVal.swap(value);
return true;
}
I'd like to write an optimized version for standard-layout classes for that there's not an overload for readFromStream, so we can exploit the memory layout of them and blit them in a single read call:
// trait is_optimization_viable is what I'm having trouble to write
template <typename T, typename std::enable_if<is_optimization_viable<T>::value, int>::type = 0>
bool readFromStream(sf::InputStream &stream, std::vector<T> &value)
{
size_t size;
if (!readFromStream(stream, VarLength(size)))
return false;
std::vector<T> newVal(size, T());
if (stream.read(newVal.data(), size*sizeof(T)) != size*sizeof(T))
return false;
newVal.swap(value);
return true;
}
Well, I could use a solution described on other answers to detect presence of a function, but there's a catch. When the type is standard-layout, I have a default readFromStream that reads like this:
template <typename T, typename std::enable_if<std::is_standard_layout<T>::value, int>::type = 0>
bool readFromStream(sf::InputStream &stream, T& value)
{
return stream.read((void*)&value, sizeof(T)) == sizeof(T);
}
So, there's always a function that does the serialization, not just the one I wanted. The problem I want to solve here is: how can I detect the presence of a non-default readFromString for type T, in order to disable the optimized version of readFromString for std::vector<T>?
I have tried to pull a few tricks. I can't limit the optimization to POD types because I'm using sf::Vector2<T> on some types I want to deserialize, which is not POD. I tried to compare the function addresses I get when I use a non-templatized and templatized function, like:
using FPtr = bool(*)(sf::InputStream&, T&);
return (FPtr)readFromStream == (FPtr)readFromStream<T>;
But, strangely enough, it didn't work. And I researched a lot of solutions, but none I could adapt to what I needed. Maybe it's not possible in C++, and I'll have to resort "marking" the types I don't want to be optimized. Or maybe it's some obscure template I haven't thought of. How could I do this?
As I understand it your problem is:
is_optimization_viable<T>;
could be defined by:
template<typename T>
using is_optimization_viable<T> = std::is_standard_layout<T>;
but for the fact that, for certain values of T that are standard layout
you nonetheless require a custom bool readFromStream(sf::InputStream &stream, T &value),
overload which means they are not optimization-viable.
Well as you must write these custom overloads, you know what those
exceptional values of T are. Say they are types X, Y, Z.
Then you can define the trait as:
#include <type_traits>
template<typename T, typename ...Us>
struct is_one_of;
template<typename T>
struct is_one_of<T> {
static constexpr bool value = false;
};
template<typename T, typename First, typename ...Rest>
struct is_one_of<T,First,Rest...> {
static constexpr bool value =
std::is_same<T,First>::value || is_one_of<T,Rest...>::value;
};
// ^ C++17: `std::disjunction` does the job
template<typename T>
using has_custom_read_from_stream = is_one_of<T,X,Y,Z>;
template<typename T>
struct is_optimization_viable {
static constexpr bool value = std::is_standard_layout<T>::value &&
!has_custom_read_from_stream<T>::value;
};
I appreciate that you'd rather avoid the ongoing maintenance of the
hard-coded type-list X, Y, Z, and prefer somehow to SFINAE-probe
whether a call readFromStream(s, t) will be a call to one of the
custom overloads for some std::declval-ed s and t.
But that's a mirage. You tell us, there will be some overload
readFromStream(s, t) that will compile whatever the type of t.
If so, a SFINAE probe will always tell you that Yes, readFromStream(s, t)
will compile - for any T as the unqualified type of t. And you
still have to make a compiletime decision as to whether T is one of
the custom types, and if not, whether it is standard-layout.
That's all there is to the problem. To tell whether T is one of
the custom types you must either test it for identity with any one
of them disjunctively, as shown, or your must find a trait independent of their
identities that is satisfied by all and only the custom types. As you
don't tell us what those custom types are, I can't suggest any such trait,
but if you find one then it will define or replace has_custom_read_from_stream<T>.
Incidentally, I second #NirFriedman's comment: is std::standard_layout really what you mean?
I have been implementing several data structure classes that are supposed to be able to hold any type and lately I've been 'upgrading' them with type traits.
I wanted to implement a type trait that prevents very large values from being taken as a hard copy. For example in:
template<typename T>
T Max(T a, T b){ return (a < b) ? b : a; }
I would not want to take T as a value if T happens to by some chance would happen to have an outrageous size.
Instead I would want something like:
template<typename T>
T Max(ref_if_large<T>::val a, ref_if_large<T>::val b){ return (a < b) ? b : a; }
I tried implementing this myself, but I've gotten myself into one of those moments where I'm just not seeing what's wrong.
template<typename T, bool cond>
struct ref_if_true {
using V = T;
};
template<typename T>
struct ref_if_true<T, true> {
using V = T&;
};
template<typename T>
struct ref_if_large {
using val =
ref_if_true< T, ((sizeof(T)) > (12U))>::V;
};
The compiler complains about V not being defined but I can't honestly figure out why.
In the last definition add typename for the dependent type, like this: using val = typename ref_if_true<blah>::V;. But seriously, don't do this for a max function. There are enough problems with aliasing already, don't add uncertainty about whether there's going to be aliasing or not.
The Boost library's boost::call_traits<T>::param_type already does the “best param type” thing.
If one wants to avoid a dependency on Boost then of course it can make sense to invent this again (I've done it myself).
But, it's useful to at least be aware of the Boost functionality.
You need to add typename to tell the compiler that V is indeed a type.
template<typename T>
struct ref_if_large {
using val =
typename ref_if_true< T, ((sizeof(T)) > (12U))>::V;
};
By the way, consider using const references for large types. They'll allow binding to const objects and rvalues.
You could also use an alias template to avoid sprinkling the client code with typenames:
template<typename T>
using ref_if_large = typename ref_if_true<T, ((sizeof(T)) > (12U))>::V;
And if you're taking by reference, you might as well parametrize the return type.
template<typename T>
ref_if_large<T> Max(ref_if_large<T> a, ref_if_large<T> b){
return (a < b) ? b : a;
}
The specific problem is adequately answered by the other two answers here. It's worth additionally pointing out that ref_if_true is a specific version of a type trait that already exists in the standard library: std::conditional:
template <class T, bool cond>
using ref_if_true_t = std::conditional_t<cond, T&, T>;
and, as such, doesn't need to exist to begin with:
template<typename T>
using ref_if_large_t = std::conditional_t<(sizeof(T) > 12U), T&, T>;
or really probably:
template<typename T>
using ref_if_large_t = std::conditional_t<(sizeof(T) > 12U), T const&, T>;
since reference to non-const implies something different.
Note: metaprogramming is already hard. It gets harder if you blaze your own trail with your own naming conventions. Metafunctions that yield a type should name it type (not V or val).
I'm trying to specify a concept to constrain a higher kinded type that has a member function template using Concepts Lite. However I am not able to find inside the technical specification or the tutorial a clause dealing with templated statements inside a concept.
How is this done?
Example: suppose I have the higher kinded type HKT with a member function template F:
template<class T>
struct HKT {
template<class U> // this looks like e.g. rebind in std::allocators
auto F(U) -> HKT<U>;
};
and that now I want to specify a concept for constraining these higher kinded types:
template <template <class> class HKT, class T>
concept HKTWithTemplateMemberFunctionF {
return requires(HKT<T> h) { // HKT<T> is a type, h is an object
// HKT<T> needs to have a member function template that
// returns HTK<U> where the type U is to be deduced and
// it can be any type (it is unconstrained)
template<class U> // is there a syntax for this?
h.F(std::declval<U>()) -> HKT<U>;
}
}
Note that I could do something like:
template <template <class> class HKT, class T, class U>
concept HKTWithTemplateMemberFunctionF {
return requires(HKT<T> h) {
h.F(std::declval<U>()) -> HKT<U>;
}
}
but this means that I need to know U at constraint site.
I don't really care if substitution for a given U fails or not although I can see why this could be a problem: e.g. apply a constraint to be sure your function doesn't fail and then fails cause the constraint was satisfied but at instantiation time substitution failed in the member function template (would it help if the member function template was constrained?).
Long story short, right now you (I?) have to provide a specific U:
template <template <class> class HKT, class T, class U = T>
concept HKTWithTemplateMemberFunctionF {
return requires(HKT<T> h) { // HKT<T> is a type, h is an object
h.F(std::declval<U>()) -> HKT<U>;
}
}
since the compiler cannot prove for all types U that might ever exist that the member-function template will work, that is, the following is hopeless:
template <template <class> class HKT, class T>
concept HKTWithTemplateMemberFunctionF {
return requires(HKT<T> h) {
template<class U> // for all those Us that haven't been written yet...
h.F(std::declval<U>()) -> HKT<U>;
}
}
In an hypothetical designed-in-5-min concepts-lite implementation where we are able to constrain U just a little-bit:
template <template <class> class HKT, class T,
InputIterator U = InputIterator() /* imaginary syntax */ >
concept HKTWithTemplateMemberFunctionF {
return requires(HKT<T> h) {
h.F(std::declval<U>()) -> HKT<U>; // Is InputIterator enough to instantiate F?
}
}
the compiler would only need to check if a model of InputIterator is enough to instantiate h.F, which is possible even if h.F is not constrained! Additionally providing U just checks that it models InputIterator, no need to even try to check h.F for U since InputIterator is enough. This could be used to optimize compile-time performance and...
...would probably interact in surprising ways with SFINAE, since AFAIK you can have a concept-overloaded function (e.g. for InputIterator) that accepts all input iterators except that one (SFINAE! why would anyone do that?!), and thus could pass concept-checking but blow at instantiation time... sad.
Let's think about the requirements you want from your comment:
// HKT<T> needs to have a member function template that
// returns HTK<U> where the type U is to be deduced and
// it can be any type (it is unconstrained)
While Concepts requires us to base our constraints around concrete types, we can be smart in our selection of which concrete types we use. What do you mean by U is any type. Really any type at all, whatsoever? Think about the smallest possible set of constraints you have on U and let's build a type that satisfies them. This is known as an archetype of U.
My goto first thought for "any type" would actually be a semiregular type. A type that is default constructible, copyable, and assignable. All the normal goodies:
namespace archetypes {
// private, only used for concept definitions, never in real code
struct Semiregular { };
}
archetypes::Semiregular is a concrete type, so we can use it to build a concept:
template <template <class> class HKT, class T>
concept bool HKTWithTemplateMemberFunctionF =
requires(HKT<T> h, archetypes::Semiregular r) {
{h.F(r)} -> HKT<archetypes::Semiregular>
};
archetypes::Semiregular is a private type. It should not be known to HKT, and so if h.F(r) is well-formed and returns a type convertible to HKT<archetypes::Semiregular>, it's almost certainly a member function template.
The question then is, is this a good archetype? Do we need U to be semiregular, or would irregular types work too? The fewer operations that you need, the fewer should be present in your archetype. Maybe all you need is that U is movable:
namespace archetypes {
// private, only used for concept definitions, never in real code
struct Semiregular { };
struct Moveable {
Moveable() = delete;
Moveable(Moveable&& ) noexcept(false);
Moveable(Moveable const& ) = delete;
~Moveable() = default;
Moveable& operator=(Moveable const& ) = delete;
Moveable& operator=(Moveable&& ) noexcept(false);
};
}
template <template <class> class HKT, class T>
concept bool HKTWithTemplateMemberFunctionF =
requires(HKT<T> h, archetypes::Moveable m) {
{ h.F(m) } -> HKT<archetypes::Moveable>
};
We're testing the same idea - invoking F() with a type that isn't well-known and excepting the return type to reflect that, hence requiring it to be a function template. But now we're giving less functionality to the type. If F() works on any, it'll work on archetypes::Moveable.
Keep iterating on this idea until you've really pared down the required functionality to the bare minimum. Maybe you don't even need the archetype to be destructible? Writing archetypes is hard, but in cases like this, it's important to get right.
I have the following snipped of code, which does not compile.
#include <iostream>
struct A {
void foo() {}
};
struct B : public A {
using A::foo;
};
template<typename U, U> struct helper{};
int main() {
helper<void (A::*)(), &A::foo> compiles;
helper<void (B::*)(), &B::foo> does_not_compile;
return 0;
}
It does not compile since &B::foo resolves to &A::foo, and thus it cannot match the proposed type void (B::*)(). Since this is part of a SFINAE template that I am using to check for a very specific interface (I'm forcing specific argument types and output types), I would like for this to work independently of inheritances, while keeping the check readable.
What I tried includes:
Casting the second part of the argument:
helper<void (B::*)(), (void (B::*)())&B::foo> does_not_compile;
This unfortunately does not help as the second part is now not recognized as a constant expression, and fails.
I've tried assigning the reference to a variable, in order to check that.
constexpr void (B::* p)() = &B::foo;
helper<void (B::* const)(), p> half_compiles;
This code is accepted by clang 3.4, but g++ 4.8.1 rejects it, and I have no idea on who's right.
Any ideas?
EDIT: Since many comments are asking for a more specific version of the problem, I'll write it here:
What I'm looking for is a way to explicitly check that a class respects a specific interface. This check will be used to verify input arguments in templated functions, so that they respect the contract that those functions require, so that compilation stops beforehand in case the class and a function are not compatible (i.e. type traits kind of checking).
Thus, I need to be able to verify return type, argument type and number, constness and so on of each member function that I request. The initial question was the checking part of the bigger template that I'm using to verify matches.
A working solution to your problem as posted at https://ideone.com/mxIVw3 is given below - see also live example.
This problem is in a sense a follow-up of Deduce parent class of inherited method in C++. In my answer, I defined a type trait member_class that extracts a class from a given pointer to member function type. Below we use some more traits to analyse and then synthesize back such a type.
First, member_type extracts the signature, e.g. void (C::*)() gives void():
template <typename M> struct member_type_t { };
template <typename M> using member_type = typename member_type_t <M>::type;
template <typename T, typename C>
struct member_type_t <T C::*> { using type = T;};
Then, member_class extracts the class, e.g. void (C::*)() gives C:
template<typename>
struct member_class_t;
template<typename M>
using member_class = typename member_class_t <M>::type;
template<typename R, typename C, typename... A>
struct member_class_t <R(C::*)(A...)> { using type = C; };
template<typename R, typename C, typename... A>
struct member_class_t <R(C::*)(A...) const> { using type = C const; };
// ...other qualifier specializations
Finally, member_ptr synthesizes a pointer to member function type given a class and a signature, e.g. C + void() give void (C::*)():
template <typename C, typename S>
struct member_ptr_t;
template <typename C, typename S>
using member_ptr = typename member_ptr_t <C, S>::type;
template <typename C, typename R, typename ...A>
struct member_ptr_t <C, R(A...)> { using type = R (C::*)(A...); };
template <typename C, typename R, typename ...A>
struct member_ptr_t <C const, R(A...)> { using type = R (C::*)(A...) const; };
// ...other qualifier specializations
The two previous traits need more specialization for different qualifiers to be more generic, e.g. const/volatile or ref-qualifiers. There are 12 combinations (or 13 including data members); a complete implementation is here.
The idea is that any qualifiers are transferred by member_class from the pointer-to-member-function type to the class itself. Then member_ptr transfers qualifiers from the class back to the pointer type. While qualifiers are on the class type, one is free to manipulate with standard traits, e.g. add or remove const, lvalue/rvalue references, etc.
Now, here is your is_foo test:
template <typename T>
struct is_foo {
private:
template<
typename Z,
typename M = decltype(&Z::foo),
typename C = typename std::decay<member_class<M>>::type,
typename S = member_type<M>
>
using pattern = member_ptr<C const, void()>;
template<typename U, U> struct helper{};
template <typename Z> static auto test(Z z) -> decltype(
helper<pattern<Z>, &Z::foo>(),
// All other requirements follow..
std::true_type()
);
template <typename> static auto test(...) -> std::false_type;
public:
enum { value = std::is_same<decltype(test<T>(std::declval<T>())),std::true_type>::value };
};
Given type Z, alias template pattern gets the correct type M of the member pointer with decltype(&Z::foo), extracts its decay'ed class C and signature S, and synthesizes a new pointer-to-member-function type with class C const and signature void(), i.e. void (C::*)() const. This is exactly what you needed: it's the same with your original hard-coded pattern, with the type Z replaced by the correct class C (possibly a base class), as found by decltype.
Graphically:
M = void (Z::*)() const -> Z + void()
-> Z const + void()
-> void (Z::*)() const == M
-> SUCCESS
M = int (Z::*)() const& -> Z const& + int()
-> Z const + void()
-> void (Z::*)() const != M
-> FAILURE
In fact, signature S wasn't needed here, so neither was member_type. But I used it in the process, so I am including it here for completeness. It may be useful in more general cases.
Of course, all this won't work for multiple overloads, because decltype doesn't work in this case.
If you simply want to check the existence of the interface on a given type T, then there're better ways to do it. Here is one example:
template<typename T>
struct has_foo
{
template<typename U>
constexpr static auto sfinae(U *obj) -> decltype(obj->foo(), bool()) { return true; }
constexpr static auto sfinae(...) -> bool { return false; }
constexpr static bool value = sfinae(static_cast<T*>(0));
};
Test code:
struct A {
void foo() {}
};
struct B : public A {
using A::foo;
};
struct C{};
int main()
{
std::cout << has_foo<A>::value << std::endl;
std::cout << has_foo<B>::value << std::endl;
std::cout << has_foo<C>::value << std::endl;
std::cout << has_foo<int>::value << std::endl;
return 0;
}
Output (demo):
1
1
0
0
Hope that helps.
Here's a simple class that passes your tests (and doesn't require a dozen of specializations :) ). It also works when foo is overloaded. The signature that you wish to check can also be a template parameter (that's a good thing, right?).
#include <type_traits>
template <typename T>
struct is_foo {
template<typename U>
static auto check(int) ->
decltype( static_cast< void (U::*)() const >(&U::foo), std::true_type() );
// ^^^^^^^^^^^^^^^^^^^
// the desired signature goes here
template<typename>
static std::false_type check(...);
static constexpr bool value = decltype(check<T>(0))::value;
};
Live example here.
EDIT :
We have two overloads of check. Both can take a integer literal as a parameter and because the second one has an ellipsis in parameter list it'll never be the best viable in overload resolution when both overloads are viable (elipsis-conversion-sequence is worse than any other conversion sequence). This lets us unambiguously initialize the value member of the trait class later.
The second overload is only selected when the first one is discarded from overload set. That happens when template argument substitution fails and is not an error (SFINAE).
It's the funky expression on the left side of comma operator inside decltype that makes it happen. It can be ill-formed when
the sub-expression &U::foo is ill-formed, which can happen when
U is not a class type, or
U::foo is inaccesible, or
there is no U::foo
the resulting member pointer cannot be static_cast to the target type
Note that looking up &U::foo doesn't fail when U::foo itself would be ambiguous. This is guaranteed in certain context listed in C++ standard under 13.4 (Address of overloaded function, [over.over]). One such context is explicit type conversion (static_cast in this case).
The expression also makes use of the fact that T B::* is convertible to T D::* where D is a class derived from B (but not the other way around). This way there's no need for deducing the class type like in iavr's answer.
value member is then initialized with value of either true_type or false_type.
There's a potential problem with this solution, though. Consider:
struct X {
void foo() const;
};
struct Y : X {
int foo(); // hides X::foo
};
Now is_foo<Y>::value will give false, because name lookup for foo will stop when it encounters Y::foo. If that's not your desired behaviour, consider passing the class in which you wish to perform lookup as a template parameter of is_foo and use it in place of &U::foo.
Hope that helps.
I suggest using decltype to generically determine the type of the member function pointers:
helper<decltype(&A::foo), &A::foo> compiles;
helper<decltype(&B::foo), &B::foo> also_compiles;
It may seem like a DRY violation, but repeating the name is fundamentally no worse than specifying the type separately from the name.