SFINAE Ambiguity - c++

Looking to get some detailed explanations as to why the following is allowed / disallowed.
#include <type_traits>
#include <iostream>
template<typename T>
std::enable_if_t<std::is_arithmetic_v<T>> foo(T v) {
std::cout << "arithmetic version called\n";
}
template<typename T>
std::enable_if_t<!std::is_arithmetic_v<T>> foo(T v) {
std::cout << "non arithmetic version called\n";
}
int main() {
foo(33);
foo("hello");
}
In relation to the above code sample, why is this allowed as overload resolution would not kick in as the functions differ only by return type?
Following on from the above, with Expression SFINAE I have to make sure the i disambiguate the overloads as in the following code snippet.
#include <type_traits>
#include <iostream>
template<typename T, std::enable_if_t<std::is_arithmetic_v<T>>* p = nullptr>
void foo(T v) {
std::cout << "arithmetic version called\n";
}
template<typename T, std::enable_if_t<!std::is_arithmetic_v<T>>* p = nullptr>
void foo(T v) {
std::cout << "non arithmetic version called\n";
}
int main() {
foo(33);
foo("hello");
}
So I guess this question is more of a why it works, ie language rules vs how to do it. I have been using these techniques for a while and often still scratch my head on which bits of the languages rules enable this.
Thanks in advance.
#include <type_traits>
#include <iostream>
#include <string>
template<typename T>
auto increment_or_self_(T a, int i) -> std::decay_t<decltype(++std::declval<T&>(), std::declval<T>())> {
++a;
return a;
}
template<typename T>
auto increment_or_self_(T a, ...) -> T {
return a;
}
template<typename T>
T increment_or_self(T a) {
return increment_or_self_(a, 0);
}
int main() {
std::cout << increment_or_self(33) << std::endl;
std::cout << increment_or_self(std::string("hello")) << std::endl;
}
In the above increment_or_self has a dummy arg to ensure the overloads are not ambiguous.
Also why do we have to use pointers when we move the EnableIf as shown in the following?

Your first example has no problems with overload resolution, because you do not have two times the same function with different return types, because the SFINAE stuff already disables one of both in each of the instantiations. Even if your SFINAE expression will result in two different return types, it simply doesn't matter, because both instantiated functions will have different signatures as they have different input parameters.
I modified your example and this will also be well formed ( see different return types with this change )
std::enable_if_t<std::is_arithmetic_v<T>, float> foo(T ) {
std::cout << "arithmetic version called\n";
return 1.1;
}
template<typename T>
std::enable_if_t<!std::is_arithmetic_v<T>, int> foo(T ) {
std::cout << "non arithmetic version called\n";
return 1;
}
int main() {
foo(33);
foo("hello");
std::cout << std::is_same_v< float, decltype(foo(33))> << std::endl;
std::cout << std::is_same_v< int, decltype(foo("Hallo"))> << std::endl;
}
Your question: "Also why do we have to use pointers when we move the EnableIf as shown in the following?"
Quite simple: The std::enable_if with only one parameter ends in a void. And you simply can't assign a value to void. You can change your code to std::enable_if_t<!std::is_arithmetic_v<T>, bool> p = true as now your std::enable_if results in a bool where you can assign a value like true.
And with C++20 you can simplify a lot by using concepts
template <typename T> concept arithmetic = std::is_arithmetic_v<T>;
template< arithmetic T>
void foo(T) {
std::cout << "arithmetic version called\n";
}
void foo(auto) {
std::cout << "non arithmetic version called\n";
}
int main() {
foo(33);
foo("hello");
}
As you can see, we do not need to define a "non-arithmetic" concept, as we simply can trust on the rule, that the more specified template will be used, if more templates are able to be instantiated.

Note you didn't specify different return values, both functions return a void.
So there is no problem there. SFINAE will just ensure one of the functions will not result in a compilable return type (but will not give an error, since substition is not an error).
Don't get to focused on SFINAE, c++ keeps improving meta template programming techniques and in this case I would just use an if constexpr solution. Like this:
#include <type_traits>
#include <iostream>
template<typename T>
void foo(const T& v) // <== I prefer not to use pass by value for template parameters so I pass by reference (avoid copying)
{
if constexpr (std::is_arithmetic_v<T>)
{
std::cout << "arithmetic version called\n";
}
else
{
std::cout << "non arithmetic version called\n";
}
}
int main()
{
foo(33);
foo("hello");
return 0;
}

Related

C++ different using declarations for different concepts

Let's say, I have my List<T> class. I have a lot of functions where I have to pass a single object of my T type. For instance
void add(const T& item)
{
...
}
and it makes sense if T is some class or a struct. However, if T is a byte or integer, it's pointless or even wrong to pas it via reference, since memory pointer costs 8 bytes (4 on 32 bit system), i.e. I pass 1 byte size data type via 8 byte size pointer.
And so I decided to define argument data type using using directive. Kind of:
using argType = const T&; requires sizeof(T) > 8
using argType = T; requires sizeof(T) <= 8
But, obviously, this code doesn't work. Can you, please, propose me other solutions for that?
It sounds like what you need is conditional_t:
#include <type_traits>
template<class T>
class List {
using argType = std::conditional_t<(sizeof(T) > 8), const T&, T>;
void add(argType item) { }
};
To add to the answer provided here: https://stackoverflow.com/a/69864339/2963099 where conditional_t was suggested
It is important to make sure that items with non-trivial copy constructors are passed by reference, since a value copy can be very expensive. As such I would limit pass by value to trivially copyable objects.
Also using sizeof(T*) will work more generically than a hardcoded 8
Note that args are reversed and the test is to use pass by value, and objects with sizeof == 8 can still be pass by reference if needed.
using argType = std::conditional_t<
(sizeof(T) <= sizeof(T *)) && std::is_trivially_copyable_v<T>,
T, const T&>;
An example of usage:
#include <type_traits>
#include <vector>
#include <iostream>
struct Test
{
Test() : x{0}
{
std::cout << "Default construct" << std::endl;
}
Test(const Test& rhs) : x{rhs.x}
{
std::cout << "copy: " << x << std::endl;
}
int * x;
};
template<class T>
struct List {
using argType = std::conditional_t<(sizeof(T) <= sizeof(T *)) && std::is_trivially_copyable_v<T>, T, const T&>;
static argType copy(argType item)
{
std::cout << "Running Copy" << std::endl;
return item;
}
};
auto foo(typename List<Test>::argType t)
{
return List<Test>::copy(t);
}
auto goo(typename List<unsigned long long>::argType t)
{
return List<unsigned long long>::copy(t);
}
int main()
{
Test t;
std::cout << "Calling Test Copy" << std::endl;
foo(t);
std::cout << "After Test Copy" << std::endl;
unsigned long long u;
std::cout << "Calling ULL Copy" << std::endl;
goo(u);
std::cout << "After ULL Copy" << std::endl;
}
see it here: https://godbolt.org/z/WTKT8G6rh
Note, you can see the parameter of foo() and goo() easily here
Note how Test would have been passed by value and incurred a possibly expensive copy, but checking for an expensive copy prevents this
With c++20 concepts you can add some constraints to your templates,
from en.cppreference.com/w/cpp/language/constraints
Class templates, function templates, and non-template functions (typically members of class templates) may be associated with a constraint, which specifies the requirements on template arguments, which can be used to select the most appropriate function overloads and template specializations.
Named sets of such requirements are called concepts. Each concept is a predicate, evaluated at compile time, and becomes a part of the interface of a template where it is used as a constraint:
#include <string>
#include <cstddef>
#include <concepts>
// Declaration of the concept "Hashable", which is satisfied by any type 'T'
// such that for values 'a' of type 'T', the expression std::hash<T>{}(a)
// compiles and its result is convertible to std::size_t
template<typename T>
concept Hashable = requires(T a) {
{ std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;
};
struct meow {};
// Constrained C++20 function template:
template<Hashable T>
void f(T) {}
//
// Alternative ways to apply the same constraint:
// template<typename T>
// requires Hashable<T>
// void f(T) {}
//
// template<typename T>
// void f(T) requires Hashable<T> {}
int main() {
using std::operator""s;
f("abc"s); // OK, std::string satisfies Hashable
//f(meow{}); // Error: meow does not satisfy Hashable
}
In your case I think you can constraint your template as follows:
template<typename T>
concept InfTo8Bytes = requires(T a) {
sizeof (T) <= 8;
};
and use you concepts as follows:
template<InfTo8Bytes T>
class Foo {
};

std::forward with templated overloaded function

I've got no idea why compiler gives me warnings about template instantiations.
Thats a piece of code which runs just fine and outputs lvalue/rvalue properly:
//template<typename T>
void overloaded(const /*T*/std::string& in)
{
std::cout << "lvalue" << std::endl;
}
//template<typename T>
void overloaded(/*T*/std::string&& in)
{
std::cout << "rvalue" << std::endl;
}
template<typename T>
void pass(T&& in)
{
overloaded(std::forward<T>(in));
}
int main()
{
std::string a;
pass(a);
pass(std::move(a));
getchar();
}
But i need to use it with templated type. So modifying the "overloaded" functions to
template<typename T>
void overloaded(const T& in)
{
std::cout << "lvalue" << std::endl;
}
template<typename T>
void overloaded(T&& in)
{
std::cout << "rvalue" << std::endl;
}
Gives template instantiations warnings, (when to me its clear T should be std::string), and console outputs rvalue 2 times instead of lvalue first.
What am i doing wrong?
Templates such as T&& are special. They are called "forwarding references". They have special deduction rules for functions like:
template<typename T>
void overloaded(T&& in)
Assume for a moment that overloaded is not overloaded. If you pass an lvalue expression of type std::string to overloaded, T will deduce as std::string&. If you pass an rvalue expression of type std::string to overloaded, T will deduce to std::string. You can use this knowledge to do this:
template<typename T>
void overloaded(T&& in)
{
if (std::is_lvalue_reference<T>::value)
std::cout << "lvalue" << std::endl;
else
std::cout << "rvalue" << std::endl;
}
In general, it is an anti-pattern to overload T&& templates with anything else. These special templates come in handy when you want to catch everything.

Passing by value all types but string to a template function

I want to define a template function that gets one argument passed by value for all types but std::string (and const char*).
template<typename T>
void foo( T value )
{
// some code using value
}
The std::string version should behave exactly as the template version, but have its parameter passed by const&.
What is the best approach to do what I want without duplicating the body of foo()?
The best I was able to think is to wrap the code using value inside another function, and then call it inside all versions of foo() (the template version and the std::string overload). Is there another way? For example, is it possible to call the template version from within the std::string overload?
EDIT
What I want to know is a good rule of thumb for avoiding code duplication among various specializations and overloads. What is a good pattern to follow? Shall I define a wrapper function for the body and then call that from within all overloads/specializations, or there is another way?
In order to avoid code duplication, the answer by 101010 can be extended to actually call the template from within the overload:
#include <string>
#include <iostream>
#include <type_traits>
#include <boost/core/demangle.hpp>
template<typename T>
void foo( T value )
{
std::cout << "inside template" << std::endl;
std::cout << boost::core::demangle(typeid(value).name()) << std::endl;
}
void foo(const std::string &value)
{
std::cout << "inside const string overload" << std::endl;
foo<const std::string&>(value);
}
int main()
{
foo(10);
foo(std::string("hello"));
return 0;
}
output
inside template
int
inside const string overload
inside template
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >
live example
Simple solution: provide an overload for std::string:
void foo( std::string const &value ) {
// some code using value
}
I think what you are looking for is rvalue signature in C++ 11.
Its as simple as:
#include <iostream>
#include <string>
template<typename T>
void foo(T&& value)
{
std::cout << "was passed by refernece:" << std::is_lvalue_reference<T&&>::value << std::endl;
std::cout << value << std::endl;
}
int main()
{
std::string text = "hello";
foo(text);
foo(1);
}
You can either pass the parameter by reference or by value and the rvalue rules will use the appropriate type.
You can define a type-trait-like class that will convert std::string to std::string& and will keep the type for all other types:
template<class T>
struct helper {
typedef T type;
};
template<>
struct helper<std::string> {
typedef std::string& type; // or const std::string& type if you want
};
template<typename T>
void foo( typename helper<T>::type value, T value2 )
{
value = value2;
}
int main()
{
int a = 10;
foo(a, 42);
std::cout << a << std::endl; // prints 10
std::string s = "abc";
foo(s, std::string("def"));
std::cout << s << std::endl; // prints def
}
Full example: http://coliru.stacked-crooked.com/a/96cf78e6c4846172
UPD: as noted by #PiotrSkotnicki, having only one parameter makes type-deduction fail. However, I will keep the answer as it might be helpful in case you indeed have several parameters of type T or if you are ok with specifying explicit template parameter to foo.
UPD2: To solve the type-deduction problem, you may add another wrapper:
template<typename T>
void foo_helper( typename helper<T>::type value )
{
value = T();
}
template<typename T>
void foo(T& value)
{
foo_helper<T>(value);
}
This still might have some problems, so whether this is applicable to your usecase, is up to you to decide.
use std::enable_if + std::is_convertibale:
template<typename T>
typename std::enable_if<!std::is_convertible<T,std::string>::value>::type foo( T value )
{
// some code using value
}

Conditional compilation and non-type template parameters

I am having trouble understanding non-type template arguments and was hoping someone could shed light on this.
#include <iostream>
template<typename T, int a>
void f() {
if (a == 1) {
std::cout << "Hello\n";
} else {
T("hello");
}
}
int main() {
f<int, 1>;
}
When I compile this, I get an error saying:
/tmp/conditional_templates.cc:13:12: required from here
/tmp/conditional_templates.cc:8:5: error: cast from ‘const char*’ to ‘int’ loses precision [-fpermissive]
T("hello");
^
But, can't the compiler detect that the non-type argument "a" is 1 and hence the else branch won't be taken? Or is that too much to expect? In which case, how do I accomplish something like this?
I have to admit I honestly don't see the fundamental reason to do this, but its your code. Apart from the obvious bug (failure to provide parens for the function call in main(), and the warning (loss of data converting a char address to int), the bigger question regarding conditional inclusion is important.
If you have have code such as:
if (something)
{
do something
}
it obviously has to compile, and will not do so conditionally. That the something is sourced from a non-type template parameter makes no difference. You need to get the logic out of an in-function if-expression and into a template-expansion controlling mechanic instead. Specialization is one such technique, SFINAE is another:
#include <iostream>
#include <iomanip>
#include <type_traits>
#include <cstdint>
static const char* something = "something";
template<class T, bool a>
typename std::enable_if<a>::type f()
{
std::cout << __PRETTY_FUNCTION__ << '\n';
std::cout << something << '\n';
}
template<class T, bool a>
typename std::enable_if<!a>::type f()
{
std::cout << __PRETTY_FUNCTION__ << '\n';
std::cout << std::hex << T(something) << '\n';
}
int main()
{
f<int, true>();
f<intptr_t, false>();
}
Output
typename std::enable_if<a>::type f() [T = int, a = true]
something
typename std::enable_if<!a>::type f() [T = long, a = false]
100001f18
What you do in each is up to you. Of course you could punt and do much/all of this with preprocessor macros, but where's the fun in that?
Try this instead:
#include <iostream>
template<typename T, int a>
struct A {
void f() {
T("hello");
}
};
template<typename T>
struct A<T,1> {
void f() {
std::cout << "Hello\n";
}
};
int main() {
A<int,1> a;
a.f();
A<int,2> b;
b.f();
}
Now, this uses partial template specialization in order to provide alternative implementations for specific values of the template parameters.
Note that I've used a class, because function templates cannot be partially specialized
Seems that in your environment sizeof(int) is different than sizeof(const char*).
In such case part of code: T("hello") which is in fact int("hello") tries to convert const char* to int. Compiler is complaining because precision would be lost in such a conversion.
To check the sizes of int and const char*:
std::cout << sizeof(int) << std::endl;
std::cout << sizeof(const char*) << std::endl;
else branch won't be run when calling f<int, 1>() but it does not mean that compiler will ignore errors within else code.
In 2022 this is not a big secret anymore, however I still thought this is worth mentioning.
With constexpr if in C++17 the tool you actually wanted to use arrived. Each discarded constexpr if statement inside a templated entity is not instantiated when the template is instantiated. (exceptions apply)
Your snippet only needs minor modifications. (apart from fixes) Mainly the branching had to be inverted to get the troublesome statement into the constexpr if statement.
#include <iostream>
template<typename T, int a>
void f() {
if constexpr (a != 1) {
T("foo");
} else {
std::cout << "bar\n";
}
}
struct Printer
{
Printer(char const * const msg) {
std::cout << "Message: " << msg << '\n';
}
};
int main() {
f<int, 1>();
f<Printer, 0>();
}
This prints
bar
Message: foo

Detect operator support with decltype/SFINAE

A (somewhat) outdated article explores ways to use decltype along with SFINAE to detect if a type supports certain operators, such as == or <.
Here's example code to detect if a class supports the < operator:
template <class T>
struct supports_less_than
{
static auto less_than_test(const T* t) -> decltype(*t < *t, char(0))
{ }
static std::array<char, 2> less_than_test(...) { }
static const bool value = (sizeof(less_than_test((T*)0)) == 1);
};
int main()
{
std::cout << std::boolalpha << supports_less_than<std::string>::value << endl;
}
This outputs true, since of course std::string supports the < operator. However, if I try to use it with a class that doesn't support the < operator, I get a compiler error:
error: no match for ‘operator<’ in ‘* t < * t’
So SFINAE is not working here. I tried this on GCC 4.4 and GCC 4.6, and both exhibited the same behavior. So, is it possible to use SFINAE in this manner to detect whether a type supports certain expressions?
In C++11 the shortest most general solution I found was this one:
#include <type_traits>
template<class T, class = decltype(std::declval<T>() < std::declval<T>() )>
std::true_type supports_less_than_test(const T&);
std::false_type supports_less_than_test(...);
template<class T> using supports_less_than = decltype(supports_less_than_test(std::declval<T>()));
#include<iostream>
struct random_type{};
int main(){
std::cout << supports_less_than<double>::value << std::endl; // prints '1'
std::cout << supports_less_than<int>::value << std::endl; // prints '1'
std::cout << supports_less_than<random_type>::value << std::endl; // prints '0'
}
Works with g++ 4.8.1 and clang++ 3.3
A more general solution for arbitrary operators (UPDATE 2014)
There is a more general solution that exploits the fact that all built-in operators are also accessible (and posibly specialized) through STD operator wrappers, such as std::less (binary) or std::negate (unary).
template<class F, class... T, typename = decltype(std::declval<F>()(std::declval<T>()...))>
std::true_type supports_test(const F&, const T&...);
std::false_type supports_test(...);
template<class> struct supports;
template<class F, class... T> struct supports<F(T...)>
: decltype(supports_test(std::declval<F>(), std::declval<T>()...)){};
This can be used in a quite general way, especially in C++14, where type deduction is delayed to the operator wrapper call ("transparent operators").
For binary operators it can be used as:
#include<iostream>
struct random_type{};
int main(){
std::cout << supports<std::less<>(double, double)>::value << std::endl; // '1'
std::cout << supports<std::less<>(int, int)>::value << std::endl; // '1'
std::cout << supports<std::less<>(random_type, random_type)>::value << std::endl; // '0'
}
For unary operators:
#include<iostream>
struct random_type{};
int main(){
std::cout << supports<std::negate<>(double)>::value << std::endl; // '1'
std::cout << supports<std::negate<>(int)>::value << std::endl; // '1'
std::cout << supports<std::negate<>(random_type)>::value << std::endl; // '0'
}
(With the C++11 standard library is a bit more complicated because there is no failure on instatiating decltype(std::less<random_type>()(...)) even if there is no operation defined for random_type, one can implement manually transparent operators in C++11, that are standard in C++14)
The syntax is quite smooth. I hope something like this is adopted in the standard.
Two extensions:
1) It works to detect raw-function applications:
struct random_type{};
random_type fun(random_type x){return x;}
int main(){
std::cout << supports<decltype(&fun)(double)>::value << std::endl; // '0'
std::cout << supports<decltype(&fun)(int)>::value << std::endl; // '0'
std::cout << supports<decltype(&fun)(random_type)>::value << std::endl; // '1'
}
2) It can additionally detect if the result is convertible/comparable to a certain type, in this case double < double is supported but a compile-time false will be returned because the result is not the specified one.
std::cout << supports<std::equal_to<>(std::result_of<std::less<>(double, double)>::type, random_type)>::value << std::endl; // '0'
Note: I just tried to compile the code with C++14 in http://melpon.org/wandbox/ and it didn't work. I think there is a problem with transparent operators (like std::less<>) in that implementation (clang++ 3.5 c++14), since when I implement my own less<> with automatic deduction it works well.
You need to make your less_than_test function a template, since SFINAE stands for Substitution Failure Is Not An Error and there's no template function that can fail selection in your code.
template <class T>
struct supports_less_than
{
template <class U>
static auto less_than_test(const U* u) -> decltype(*u < *u, char(0))
{ }
static std::array<char, 2> less_than_test(...) { }
static const bool value = (sizeof(less_than_test((T*)0)) == 1);
};
int main()
{
std::cout << std::boolalpha << supports_less_than<std::string>::value << endl;
}
This is C++0x, we don't need sizeof-based tricks any more... ;-]
#include <type_traits>
#include <utility>
namespace supports
{
namespace details
{
struct return_t { };
}
template<typename T>
details::return_t operator <(T const&, T const&);
template<typename T>
struct less_than : std::integral_constant<
bool,
!std::is_same<
decltype(std::declval<T const&>() < std::declval<T const&>()),
details::return_t
>::value
> { };
}
(This is based on iammilind's answer, but doesn't require that T's operator< return-type be a different size than long long and doesn't require that T be default-constructable.)
Below simple code satisfies your requirement (if you don't want compile error):
namespace supports {
template<typename T> // used if T doesn't have "operator <" associated
const long long operator < (const T&, const T&);
template <class T>
struct less_than {
T t;
static const bool value = (sizeof(t < t) != sizeof(long long));
};
}
Usage:
supports::less_than<std::string>::value ====> true; // ok
supports::less_than<Other>::value ====> false; // ok: no error
[Note: If you want compile error for classes not having operator < than it's very easy to generate with very few lines of code.]
#xDD is indeed correct, though his example is slightly erroneous.
This compiles on ideone:
#include <array>
#include <iostream>
struct Support {}; bool operator<(Support,Support) { return false; }
struct DoesNotSupport{};
template <class T>
struct supports_less_than
{
template <typename U>
static auto less_than_test(const U* u) -> decltype(*u < *u, char(0))
{ }
static std::array<char, 2> less_than_test(...) { }
static const bool value = (sizeof(less_than_test((T*)0)) == 1);
};
int main()
{
std::cout << std::boolalpha << supports_less_than<Support>::value << std::endl;
std::cout << std::boolalpha <<
supports_less_than<DoesNotSupport>::value << std::endl;
}
And results in:
true
false
See it here in action.
The point is that SFINAE only applies to template functions.