Weird constructor SFINAE error with std::initializer_list - c++

I can't understand why the following code doesn't compile. The error message given by the compiler isn't that helpful either.
Working example:
#include <string>
#include <type_traits>
template <typename T>
struct TIteratorValue {
using Type = typename T::Type;
};
template <typename T>
struct TIteratorValue<T *> {
using Type = T;
};
template <typename T>
using IteratorValue = typename TIteratorValue<T>::Type;
template <typename T>
struct Test {
template <typename V,
std::enable_if_t<std::is_constructible_v<T, V>, int> = 0>
Test(std::initializer_list<V> const list, size_t const x = 0)
: Test(list.begin(), list.end(), x) {}
template <typename I,
std::enable_if_t<std::is_constructible_v<T, IteratorValue<I>>, int> = 0>
// Does not compile!
Test(I begin, I const end, size_t const x = 0) {}
// Compiles!
//Test(I begin, I const end, size_t const x) {}
};
int
main() {
Test<std::string> test({ "a", "b", "c" }, 10);
return 0;
}
Clang's error message:
C:\Users\joaom\Dropbox\++A\so\weird_overloading.cpp:6:24: error: type 'int' cannot be used prior to '::' because it has no members
using Type = typename T::Type;
^
C:\Users\joaom\Dropbox\++A\so\weird_overloading.cpp:15:1: note: in instantiation of template class 'TIteratorValue<int>' requested here
using IteratorValue = typename TIteratorValue<T>::Type;
^
C:\Users\joaom\Dropbox\++A\so\weird_overloading.cpp:25:50: note: in instantiation of template type alias 'IteratorValue' requested here
std::enable_if_t<std::is_constructible_v<T, IteratorValue<I>>, int> = 0>
^
C:\Users\joaom\Dropbox\++A\so\weird_overloading.cpp:27:2: note: while substituting prior template arguments into non-type template parameter
[with I = int]
Test(I begin, I const end, size_t const x = 0) {}
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\Users\joaom\Dropbox\++A\so\weird_overloading.cpp:35:20: note: while substituting deduced template arguments into function template 'Test'
[with I = int, $1 = (no value)]
Test<std::string> test({ "a", "b", "c" }, 10);
The only int is the argument x but I can't see how's that affecting the code.
Visual Studio gives me the same error too.

template <typename I, std::enable_if_t<std::is_constructible_v<T, IteratorValue<I>>, int> = 0>
// Does not compile!
Test(I begin, I const end, size_t const x = 0) {}
// Compiles!
//Test(I begin, I const end, size_t const x) {}
Both of these functions will not compile if the template is instantiated.
Since your main function attempts to construct a Test from 2 parameters, adding a default value to the third parameter simply means that this function should be considered. It allows the template to be instantiated.
I still can't understand why the following code doesn't compile.
You are defining IteratorValue<I> (and therefore I::type) before checking if I is an iterator.
Use the already defined std::iterator_traits to solve this.
// Formatted for clarity
template <typename I,
std::enable_if_t<
std::is_constructible_v<
T,
typename std::iterator_traits<I>::value_type
>,
int
> = 0>

Related

Compiler discrepancy concerning type-deduction

I'm trying to construct an object from a templated class which should be of iterator-type based on the container that is being passed through its constructor. To do so, I use a deduction guide which extracts the iterator member-type.
This seems to work on both Clang and GCC. The problem is that when making some seemingly unrelated changes to this class, the code no longer compiles on Clang (but does on some versions of GCC). Here is the code, which I unfortunately couldn't make much smaller:
#include <array>
#include <iterator>
#include <type_traits>
template<typename It>
using category_t = typename std::iterator_traits<It>::iterator_category;
//
template<typename Iter>
struct is_random_access_iter
: std::is_convertible<
category_t<Iter>,
std::random_access_iterator_tag> {};
template<typename T>
struct has_random_access_iter
: is_random_access_iter<typename T::iterator> {};
template<typename T>
struct is_unary_type
: std::conjunction<
std::is_default_constructible<T>,
std::is_copy_constructible<T>> {};
//
template<typename Iter>
inline constexpr auto is_random_access_iter_v
= bool{is_random_access_iter<Iter>{}};
template<typename T>
inline constexpr auto has_random_access_iter_v
= bool{has_random_access_iter<T>{}};
template<typename T>
inline constexpr auto is_unary_type_v
= bool{is_unary_type<T>{}};
//
template<typename It, int Size,
typename = std::enable_if_t<is_random_access_iter<It>{}>>
struct derp {
// constexpr derp() = default;
template<typename T,
typename = std::enable_if_t<is_unary_type<T>{}>,
typename = std::enable_if_t<has_random_access_iter_v<T>>>
// typename = std::enable_if_t<is_random_access_iter_v<typename T::iterator>>>
constexpr explicit derp(T& herp)
: iter{herp.begin()} {}
It iter;
};
template<typename T>
derp(T) -> derp<typename T::iterator, std::tuple_size<T>{}>;
template<typename T>
struct wrap {
constexpr wrap() = default;
constexpr explicit wrap(T& herp)
: herp{herp} {}
derp<typename T::iterator, std::tuple_size<T>{}> herp;
};
auto main() -> int {
auto arr = std::array<int, 3>{};
wrap{arr};
}
One of the problems comes to light when uncommenting the default constructor of derp. It seems as if derp is being instantiated by its own type, instead of the iterator-type from the passed-in container:
GCC ARM 7.2.1:
<source>:44:9: required by substitution of 'template<class T, class, class> constexpr derp<int*, 3, void>::derp(T&) [with T = const derp<int*, 3, void>; <template-parameter-1-2> = void; <template-parameter-1-3> = <missing>]'
<source>:66:13: required from here
<source>:16:8: error: no type named 'iterator' in 'const struct derp<int*, 3, void>'
Clang:
<source>:17:41: error: no type named 'iterator' in 'derp<int *, 3, void>'
: is_random_access_iter<typename T::iterator> {};
~~~~~~~~~~~~^~~~~~~~
<source>:44:37: note: in instantiation of template class 'has_random_access_iter<const derp<int *, 3, void>>' requested here
typename = std::enable_if_t<has_random_access_iter<T>{}>>
^
<source>:46:24: note: in instantiation of default argument for 'derp<const derp<int *, 3, void>, void>' required here
constexpr explicit derp(T& herp)
The code no longer compiles on Clang and GCC ARM 7.2.1. It does however compile on GCC trunk. Why does enabling the default constructor cause a compilation error? And why does GCC >= 11.1 seems to be unaffected?
Live example

SFINAE type trait with pointer-to-member-function fails

I'm practicing SFINAE and would like to implement a type trait in order to check if a given class T contains a method print(). I have the following two variants:
// (1)
template <typename T, typename = int>
struct has_print_method : std::false_type {};
template <typename T>
struct has_print_method<T, decltype(&T::print, 0)> : std::true_type {};
template <typename T>
const bool has_print_method_v = has_print_method<T>::value;
// (2)
template <typename T>
struct has_print_method{
template <typename U, typename = void> struct helper : std::false_type{};
template <typename U> struct helper<U, decltype(&U::print)> : std::true_type{};
static const bool value = helper<T, void (T::*)() const>::value;
};
template <typename T>
const bool has_print_method_v = has_print_method<T>::value;
(1) only checks for the existence of a member method print() and ignores the member method's signature. Whereas (2) checks the method's signature, i.e. it requires a member method void print() const.
I test both variants via:
#include <iostream>
#include <type_traits>
// Simple Class A
struct A{
int a;
public:
void print() const{}
};
// (1) or (2) here
template<typename T, std::enable_if_t<has_print_method_v<T>, bool> = true>
void print(T t) {
t.print();
}
void print(double x){
std::cout << x << '\n';
}
int main() {
A a;
print(a);
print(1.0); // (*)
return 0;
}
Using the type trait (1) and compiling with clang 12.0 and std=c++17 flag works as expected. However, using (2) instead, I obtain
<source>:28:50: error: member pointer refers into non-class type 'double'
static const bool value = helper<T, void (T::*)() const>::value;
^
<source>:32:33: note: in instantiation of template class 'has_print_method<double>' requested here
const bool has_print_method_v = has_print_method<T>::value;
^
<source>:34:39: note: in instantiation of variable template specialization 'has_print_method_v<double>' requested here
template<typename T, std::enable_if_t<has_print_method_v<T>, bool> = true>
^
<source>:35:6: note: while substituting prior template arguments into non-type template parameter [with T = double]
void print(T t) {
^~~~~~~~~~~~
<source>:47:5: note: while substituting deduced template arguments into function template 'print' [with T = double, $1 = (no value)]
print(1.0);
^
1 error generated.
What am I missing here? You can find the example here on godbolt. Edit: Um, I have just noticed that both versions compile without an error with gcc11.1. Strange.
First up, judging by your error messages and my own tests, I think you mean that type trait (1) works and (2) doesn't. On that basis, here is a version of trait (1) that tests for a matching function signature:
template <typename T, typename = int>
struct has_print_method : std::false_type {};
template <typename T>
struct has_print_method<T, decltype(&T::print, 0)> : std::is_same <decltype(&T::print), void (T::*)() const> {};
template <typename T>
const bool has_print_method_v = has_print_method<T>::value;
Live demo

Why substitution fails in this function template?

I have a set of template functions which receive an index (is an int in the example) and return a value of the given type, I've used SFINAE to separate std::string from arithmetic types:
// 1
template <typename T>
typename std::enable_if<std::is_arithmetic<T>::value, T>::type
t(int) { ... }
// 2
template <typename T>
typename std::enable_if<std::is_same<std::string, T>::value, T>::type
t(int) { ... }
// 3
template <template <typename ...> class T, typename ... P>
T<P ...> t(int) { ... }
Also, there's a function which receives a container and fills it up using the functions above:
template <typename C>
C c(int)
{
C r{};
std::insert_iterator<C> iterator(r, r.begin());
*iterator = t<typename C::value_type>(0);
return r;
}
The goal of t is to tell apart numbers and strings, but if a pair is provided (because it comes from an associative container) then, t should split each pair component in two different t calls with the first and second types.
While deserializing non-associative containers it works but using associative containers compilation fails:
using vi = std::vector<int>;
using mii = std::map<int, int>;
auto o = c<vi>(0); // Deserialize vector
auto p = c<mii>(0); // Deserialize map
The compilation fails at the point of deserializing one element of the container:
*iterator = t<typename C::value_type>(0);
For non-associative containers C::value_type is a type which mets one of the conditions of the first two versions of t, but for associative containers C::value_type is a pair and should fail for versions #1 and #2 of t but not for the #3 version of t function; the issue is that it fails for the three of them:
error: no matching function for call to 't'
*iterator = t<typename C::value_type>(0);
^~~~~~~~~~~~~~~~~~~~~~~~~
note: in instantiation of function template specialization 'c<std::map<int, int>>' requested here
auto p = c<mii>(0);
^
note: candidate template ignored: requirement 'std::is_arithmetic<pair<const int, int> >::value' was not satisfied [with T = std::pair<const int, int>]
t(int) { ... }
^
note: candidate template ignored: requirement 'std::is_same<std::string, pair<const int, int> >::value' was not satisfied [with T = std::pair<const int, int>]
t(int) { ... }
^
note: candidate template ignored: invalid explicitly-specified argument for template parameter 'T'
T<P ...> t(int) { ... }
^
Apparently the compiler is complaining about of the lack of template-template parameters but, if I get rid of SFINAE the error vanishes:
template <typename T>
T
t(int) { return {}; }
template <template <typename ...> class T, typename ... P>
T<P ...> t(int) { return {}; }
template <typename C>
C c(int)
{
C r{};
std::insert_iterator<C> iterator(r, r.begin());
*iterator = t<typename C::value_type>(0);
return r;
}
int main()
{
using vi = std::vector<int>;
using mii = std::map<int, int>;
auto o = c<vi>(0);
auto p = c<mii>(0);
// print 0
for (auto &v : o) std::cout << v << '\n';
// print 00
for (auto &v : p) std::cout << v.first << v.second << '\n';
return 0;
}
It looks like SFINAE is forcing the template-template parameter to be required instead of deduced, why is this happening? How should I solve it?
Code is available in Wandbox 三へ( へ՞ਊ ՞)へ ハッハッ.
It looks like (from your comment and edit), that you want to execute different functions depending on the given template parameters. The easiest way to do this is to use a class, since classes are much more flexible regarding specialization. Here is a small example of what you could do:
// initial declaration (without definition), the second template
// parameter will be used to enable some specializations
template <class T, class = void>
struct deserializer;
// specialization for arithmetic types
template <class T>
struct deserializer<
T, std::enable_if_t<std::is_arithmetic<T>::value>> {
T operator()() const {
}
};
// specialization for std::string
template <>
struct deserializer<std::string> {
std::string operator()() const {
}
};
// specialization for std::pair<U, V>
template <class U, class V>
struct deserializer<std::pair<U, V>> {
std::pair<U, V> operator()() const {
}
};
Then in your function c:
deserializer<typename C::value_type> ds;
*iterator = ds();
You can also add an intermediate generic function if you don't want to create an object of type deserializer each time:
template <class T>
T deserialize() {
return deserializer<T>{}();
}
But I think your goal here is to deserialize multiple objects, so having a functor is not that bad in that case.
Why does the deduction fails in your case?
Actually, there is no deduction here since deduction works with arguments and you are using a return type. The problem here is that this instantiation of t:
t<std::pair<int, int>>
...will never match this declaration of t:
template <template <class... > class, class... >
auto t();
Because you would need:
t<std::pair, int, int>
...to match such template signature. The only template signature that could be matched using t<typename C::value_type> is a signature of the form:
template <class T, /* something */>
...where /* something */ is either a variadic template parameter (class...), or a list of defaulted template parameters (class X = void, int N = 0), or a combination of both.
The problem here is that the original t and the new t have different template parameters:
// original.
template <template <typename ...> class T, typename ... P>
T<P ...> t(int) { ... }
// new.
template <typename C>
C c(int)
Note not only does the original t have (possibly) more than 1 template parameter, but the first parameter is a template template parameter, not a type parameter.
You also seem confused regarding template argument deduction. Template argument deduction deduces the template arguments from the function arguments. All of your functions have a single int parameter, so no deduction is taking place.
In other words, t<typename C::value_type>(0) can't work with the original function, because std::pair<const int, int> is not a valid template template parameter. You would need to write t<std::pair, const int, int>(0).
If your question is how to use SFINAE to accept a "container" (not really, because containers can have non-type template parameters), then this should work:
template<typename T>
struct is_container : std::false_type { };
template<template<typename...> class C, typename... Ts>
struct is_container<C<Ts...>> : std::true_type { };
template <typename T>
typename std::enable_if<is_container<T>::value, T>::type
t(int) { ... }

Disable Function when parameter type is void

I have a template class looking like this:
template <typename T> constexpr bool is_value_passable_v = is_trivially_copyable_v<T> && sizeof(T) <= sizeof(void*) && !is_polymorphic_v<T>;
template <typename B, typename T> using param_base_t = conditional_t<is_value_passable_v<B>, T, const T&>;
template <typename T> struct param_d
{
using type = param_base_t<T, T>;
};
template <> struct param_d<void>
{
using type = void;
};
template <typename T> using param_t = typename param_d<T>::type;
template <class TIn> class CClass
{
public:
static constexpr bool use_input_v = !is_same_v<typename TIn::input_t, void>;
using input_t = conditional_t<use_input_v, param_t<typename TIn::input_t>, void>;
enable_if_t<use_input_v> Input(input_t i);
};
The goal of this code is, to provde different Input functions for different template paramters.
A template parameter with input_t = int should result in void Input(int i)
A template parameter with input_t = std::vector should result in void Input(const std::vector& i)
A template parameter with input_t = void should remove the Input function
Compiling this with clang gives
/usr/bin/../include/c++/v1/type_traits:225:78: error: no type named 'type' in 'std::__1::enable_if<false, void>'; 'enable_if' cannot be used to disable this declaration
template <bool _Bp, class _Tp = void> using enable_if_t = typename enable_if<_Bp, _Tp>::type;
^~~
Edit 1:
After adding the line
template <typename T> static constexpr bool use_input2_v = use_input_v;
and replacing the function declaration with
template <typename T = void> enable_if_t<use_input2_v<T>> Input(input_t i)
clang complains there's no matching member function for call to 'Input':
note: candidate template ignored: substitution failure [with T = void]: non-type template argument is not a constant expression
template <typename T = void> enable_if_t<use_input2_v<T>> Input(input_t i);
~~~~~~~~~~~~ ^
Edit 2:
Forgot to mention, that this error comes with all three variants of template parameters.
Edit 3:
A sample use case for CClass could be
class CInput0
{
using input_t = int;
};
class CInput1
{
using input_t = std::vector<int>;
};
class CInput2
{
using input_t = void;
};
CClass<CInput0> in0;
CClass<CInput1> in1;
CClass<CInput2> in2;
std::vector<int> i = {1, 2, 3};
in0.Input(3);
in1.Input(i);
//in2.Input() disabled
For SFINAE`to work, it needs to be working off a dependent type, otherwise there is no substitution failure. Here is an example:
template <typename Self = CClass<TIn>>
typename std::enable_if<Self::use_input_v>::type
Input(typename Self::input_t) { }
When a member function is a template, the compiler conditionally creates it based on whether the template parameters work. In your original example, since the whole class is a template, but the method is not, the compiler just sees it as an error with your member function as soon as the class is instantiated. Using a default template parameter is just the trick we need. What we want to test is now considered dependent.
If you want to enable a function based on the properties of a template parameter, without partial specialization you can use the following pattern:
#include <iostream>
#include <type_traits>
#include <vector>
std::ostream& operator<<(std::ostream& out, std::vector<int>& value);
template <class TIn> class CClass
{
public:
template <class T =TIn, class PARAM= std::enable_if_t<!(std::is_same<void,typename T::input_t>::value) ,typename T::input_t > >
void Input(PARAM i){
std::cout << "Called Input with parameter: "<< i << std::endl;
}
};
struct CInput0{ using input_t = int;};
struct CInput1{ using input_t = std::vector<int>;};
struct CInput2{ using input_t = void;};
CClass<CInput0> in0;
CClass<CInput1> in1;
CClass<CInput2> in2;
std::vector<int> i = {1, 2, 3};
std::ostream& operator<<(std::ostream& out, std::vector<int>& value) {
for (auto& e:value) {
out << e;
}
out << std::endl;
return out;
}
int main() {
in0.Input(3);
in1.Input(i);
//in2.Input() disabled
}
This is a simplified version of your example, which you should be able to adapt.

Strange error when applying arguments from a vector to a function using variadic templates

I'm trying to make a function that takes a function and a vector and applies the function to the arguments from the vector, something like
f([](int x, int y) {return x+y;}, {1, 2}); \\returns 3
my current code looks like this:
#include <iostream>
#include <vector>
#include <functional>
#include <type_traits>
#include <boost/type_traits.hpp>
template <std::size_t... Indices>
struct indices {
using next = indices<Indices..., sizeof...(Indices)>;
};
template <std::size_t N>
struct build_indices {
using type = typename build_indices<N-1>::type::next;
};
template <>
struct build_indices<0> {
using type = indices<>;
};
template <std::size_t N>
using BuildIndices = typename build_indices<N>::type;
template <size_t num_args>
struct unpack_caller
{
private:
template <typename FuncType, size_t... I>
typename std::result_of<FuncType>::type call(FuncType &f, std::vector<int> &args, indices<I...>){
return f(args[I]...);
}
public:
template <typename FuncType>
typename std::result_of<FuncType>::type operator () (FuncType &f, std::vector<int> &args)
{
return call(f, args, BuildIndices<num_args>{});
}
};
template<typename InnerType, typename ...Args>
class MFA
{
std::function<InnerType(Args...)> func;
public:
MFA(InnerType f(Args...))
: func(f)
{}
virtual InnerType calc(std::vector<InnerType> & x)
{
return unpack_caller<sizeof...(Args)>()(func, x);
}
};
int main()
{
auto fun1 = MFA<int, int, int>([](int x, int y) {return x + y; });
std::vector<int> arg = std::vector<int>({1, 2});
fun1.calc(arg);
return 0;
}
and it gives an error (GCC 4.8.1):
$g++ -std=c++0x main.cpp -o demo -lm -pthread -lgmpxx -lgmp -lreadline 2>&1
main.cpp: In instantiation of 'InnerType MFA<InnerType, Args>::calc(std::vector<InnerType>&) [with InnerType = int; Args = {int, int}]':
main.cpp:68:18: required from here
main.cpp:57:50: error: no match for call to '(unpack_caller<2ul>) (std::function<int(int, int)>&, std::vector<int>&)'
return unpack_caller<sizeof...(Args)>()(func, x);
^
main.cpp:25:8: note: candidate is:
struct unpack_caller
^
main.cpp:35:45: note: template<class FuncType> typename std::result_of<FuncType>::type unpack_caller<num_args>::operator()(FuncType&, std::vector<int>&) [with FuncType = FuncType; long unsigned int num_args = 2ul]
typename std::result_of<FuncType>::type operator () (FuncType &f, std::vector<int> &args)
^
main.cpp:35:45: note: template argument deduction/substitution failed:
main.cpp: In substitution of 'template<class FuncType> typename std::result_of<FuncType>::type unpack_caller<num_args>::operator()(FuncType&, std::vector<int>&) [with FuncType = FuncType; long unsigned int num_args = 2ul] [with FuncType = std::function<int(int, int)>]':
main.cpp:57:50: required from 'InnerType MFA<InnerType, Args>::calc(std::vector<InnerType>&) [with InnerType = int; Args = {int, int}]'
main.cpp:68:18: required from here
main.cpp:35:45: error: invalid use of incomplete type 'class std::result_of<std::function<int(int, int)> >'
In file included from /usr/local/gcc-4.8.1/include/c++/4.8.1/bits/move.h:57:0,
from /usr/local/gcc-4.8.1/include/c++/4.8.1/bits/stl_pair.h:59,
from /usr/local/gcc-4.8.1/include/c++/4.8.1/bits/stl_algobase.h:64,
from /usr/local/gcc-4.8.1/include/c++/4.8.1/bits/char_traits.h:39,
from /usr/local/gcc-4.8.1/include/c++/4.8.1/ios:40,
from /usr/local/gcc-4.8.1/include/c++/4.8.1/ostream:38,
from /usr/local/gcc-4.8.1/include/c++/4.8.1/iostream:39,
from main.cpp:1:
/usr/local/gcc-4.8.1/include/c++/4.8.1/type_traits:1878:11: error: declaration of 'class std::result_of<std::function<int(int, int)> >'
class result_of;
How can I correct it? The function MFA::calc must have the type it has now as MFA is supposed to be an implementation of an interface.
EDIT: I've been using code from following stackoverflow questions:
Any Solution to Unpack a Vector to Function Arguments in C++?
Initialize std::array with a range (pair of iterators)
You can't use std::result_of<FuncType>::type, you would need to add the arguments, e.g. std::result_of<FuncType(int,int)>::type. In your case, this is a more difficult than the alternative, hence I suggest you use:
template <size_t num_args>
struct unpack_caller
{
private:
template <typename FuncType, size_t... I>
auto call(FuncType &f, std::vector<int> &args, indices<I...>)
-> decltype( f(args[I]...) )
{
return f(args[I]...);
}
public:
template <typename FuncType>
auto operator () (FuncType &f, std::vector<int> &args)
-> decltype( std::declval<unpack_caller<num_args>>().call(f, args, BuildIndices<num_args>{}) )
{
return call(f, args, BuildIndices<num_args>{});
}
};
Live example
Note that with C++14, you could leave out the -> decltype(...) part and simply rely on auto alone.