Variadic template argument deduction fails when passing initializer lists - c++

Bar holds a std::vector of std::pairs of std::arrays of FooValueAdaptors.
FooValueAdaptor implicitly converts int to bool to FooValue, which makes little sense in this contrived example, but perfect sense in my application.
I implemented a convenience function Bar::addEntries for adding multiple entries at once, but calling it with more than two arguments fails to compile using GCC 4.8.0. See the error messages below.
#include <array>
#include <utility>
#include <vector>
enum class FooValue {
A,
B,
C
};
class FooValueAdaptor {
public:
FooValueAdaptor(bool value)
: m_value(static_cast<FooValue>(value)) {
}
FooValueAdaptor(int value)
: m_value(static_cast<FooValue>(static_cast<bool>(value))) {
}
FooValueAdaptor(FooValue value)
: m_value(value) {
}
operator FooValue() {
return m_value;
}
operator bool() {
return m_value == FooValue::C;
}
private:
FooValue m_value;
};
template<std::size_t nFirst, std::size_t nSecond>
class Bar {
public:
typedef std::array<FooValueAdaptor, nFirst> First;
typedef std::array<FooValueAdaptor, nSecond> Second;
typedef std::pair<First, Second> Entry;
Bar()
: m_table() {
}
void addEntry(First first, Second second) {
m_table.push_back(std::make_pair(first, second));
}
template <typename... Args>
void addEntries() {
}
template <typename... Args>
void addEntries(First first, Second second, Args... args) {
addEntry(first, second);
addEntries(args...);
}
private:
std::vector<Entry> m_table;
};
int main(int argc, char **argv) {
Bar<2, 1> b;
b.addEntry({ 0, 0 }, { 0 });
b.addEntries(
{ 0, 1 }, { 0 },
{ 1, 0 }, { 0 },
{ 1, 1 }, { 1 }
);
return 0;
}
Compiler error messages:
test.cpp: In function ‘int main(int, char**)’:
test.cpp:74:2: error: no matching function for call to ‘Bar<2ul, 1ul>::addEntries(<brace-enclosed initializer list>, <brace-enclosed initializer list>, <brace-enclosed initializer list>, <brace-enclosed initializer list>, <brace-enclosed initializer list>, <brace-enclosed initializer list>)’
);
^
test.cpp:74:2: note: candidates are:
test.cpp:53:7: note: template<class ... Args> void Bar<nFirst, nSecond>::addEntries() [with Args = {Args ...}; long unsigned int nFirst = 2ul; long unsigned int nSecond = 1ul]
void addEntries() {
^
test.cpp:53:7: note: template argument deduction/substitution failed:
test.cpp:74:2: note: candidate expects 0 arguments, 6 provided
);
^
test.cpp:57:7: note: void Bar<nFirst, nSecond>::addEntries(Bar<nFirst, nSecond>::First, Bar<nFirst, nSecond>::Second, Args ...) [with Args = {}; long unsigned int nFirst = 2ul; long unsigned int nSecond = 1ul; Bar<nFirst, nSecond>::First = std::array<FooValueAdaptor, 2ul>; Bar<nFirst, nSecond>::Second = std::array<FooValueAdaptor, 1ul>]
void addEntries(First first, Second second, Args... args) {
^
test.cpp:57:7: note: candidate expects 2 arguments, 6 provided
How can I help the compiler's deduction along?

You need to tell the compiler explicitly what you need:
void addEntries(std::initializer_list<std::pair<First, Second>> il) {
for( const auto& e : il ) {
addEntry(e.first,e.second);
}
}
and call it like this:
b.addEntry({{ 0, 0 }}, {{ 0 }});
b.addEntries({
{{{ 0, 1 }}, {{ 0 }}},
{{{ 1, 0 }}, {{ 0 }}},
{{{ 1, 1 }}, {{ 1 }}}
});
Notice the huge amount of curly brackets, but I think the above is actually the only correct syntax. Fewer brackets are accepted by both GCC 4.8 and Clang 3.2, but Clang gives lots of warnings, the above fixes that. Some people are already working on a "fix", but that'll take some time.

Related

Can I use SFINAE to compare enum class Value?

Live On Coliru
struct Banana : public FruitBase
{
Banana(Fruit fruit, int price, std::string name, int length):
...
};
struct Apple : public FruitBase
{
Apple(Fruit fruit, int price, std::string name):
...
};
template<typename... Ts>
std::unique_ptr<FruitBase> makeFruits(Fruit fruit, Ts&&... params)
{
switch(fruit)
{
case Fruit::APPLE:
return std::make_unique<Apple>(Fruit::APPLE, std::forward<Ts>(params)...);
break;
case Fruit::BANANA:
return std::make_unique<Banana>(Fruit::BANANA, std::forward<Ts>(params)...);
break;
}
return nullptr;
}
int main()
{
auto apple_ptr = makeFruits(Fruit::APPLE, 10, "gala");
print_fruit(apple_ptr.get());
auto banana_ptr = makeFruits(Fruit::BANANA, 20, "plantains", 123);
print_fruit(banana_ptr.get());
}
main.cpp:37:5: note: candidate: 'Apple::Apple(Fruit, int, std::string)'
37 | Apple(Fruit fruit, int price, std::string name):
| ^~~~~
main.cpp:37:5: note: candidate expects 3 arguments, 4 provided
main.cpp:26:5: note: candidate: 'Banana::Banana(Fruit, int, std::string, int)'
26 | Banana(Fruit fruit, int price, std::string name, int length):
| ^~~~~~
main.cpp:26:5: note: candidate expects 4 arguments, 3 provided
Question> The issue is that the compiler has problems choosing which constructor for Apple and which one for Banana. I assume I could use SFINAE(i.e. with help of std::enable_if) to help compiler deduce the correct function. However, I didn't find an example where the checking is for VALUE instead of TYPE. Can someone give me a little hint here?
Even if not taken, all branches should be valid
If argument is passed as template parameter (so known at compile time), you might do
template<Fruit fruit, typename... Ts>
std::unique_ptr<FruitBase> makeFruits(Ts&&... params)
{
if constexpr (fruit == Fruit::APPLE)
return std::make_unique<Apple>(Fruit::APPLE, std::forward<Ts>(params)...);
else if constexpr (fruit == Fruit::BANANA)
return std::make_unique<Banana>(Fruit::BANANA, std::forward<Ts>(params)...);
else
return nullptr;
}
int main()
{
auto apple_ptr = makeFruits<Fruit::APPLE>(10, "gala");
auto banana_ptr = makeFruits<Fruit::BANANA>(20, "plantains", 123);
}
Demo
You can still add SFINAE on top of that.

Template argument deduction fails when passing enum

I'm trying to insert an enum parameter into a constexpr function. I have done this in the past and it always worked... except in this case. This case is only special in that I'm calling a factory function first. But apparently C++ doesn't see through this. What can be done?
Those are my errors:
<source>: In function 'constexpr auto operator+(some_enum)':
<source>:30:28: error: no matching function for call to 'signal<1, state>(std::nullptr_t)'
30 | return signal<1, state>(nullptr);
| ~~~~~~~~~~~~~~~~^~~~~~~~~
<source>:23:37: note: candidate: 'template<bool set, some_enum S, class ... Ts> constexpr signal_str<sizeof... (Ts)> signal(Ts ...)'
23 | constexpr signal_str<sizeof...(Ts)> signal(Ts... Args)
| ^~~~~~
<source>:23:37: note: template argument deduction/substitution failed:
<source>:30:28: error: 'state' is not a constant expression
30 | return signal<1, state>(nullptr);
| ~~~~~~~~~~~~~~~~^~~~~~~~~
<source>:30:28: note: in template argument for type 'some_enum'
<source>:28:16: error: invalid return type 'auto' of 'constexpr' function 'constexpr auto operator+(some_enum)'
28 | constexpr auto operator+(some_enum state)
| ^~~~~~~~
Compiler returned: 1
This is my code:
#include <array>
#include <cstdint>
#include <iostream>
typedef void* TaskType_t;
enum some_enum
{
SOME_STATE = 1,
};
template <size_t N>
struct signal_str
{
uint32_t val_;
std::array<TaskType_t, N> tasks_;
};
template <bool set, some_enum S, typename... Ts>
constexpr signal_str<sizeof...(Ts)> signal(Ts... Args)
{
return signal_str<sizeof...(Ts)>{S, {Args...}}.val_;
}
constexpr auto operator+(some_enum state)
{
return signal<1, state>(nullptr);
}
int main()
{
static_assert(+SOME_STATE);
}
I'm using C++17 on xtensa-gcc 8.2.0 but it's the same with gcc 11 (LIVE DEMO).
EDIT: This problem is different from "Why is const variable necessary for template specialization over constants" because enums are already constants. To showcase this the following DOES actually compile:
#include <array>
#include <cstdint>
#include <iostream>
typedef void* TaskType_t;
enum some_enum
{
SOME_STATE = 1,
};
template <size_t N>
struct signal_str
{
uint32_t val_;
std::array<TaskType_t, N> tasks_;
};
constexpr auto operator+(some_enum state)
{
return signal_str<1>{state, nullptr}.val_;
}
int main()
{
static_assert(+SOME_STATE);
}
DEMO
So the problem IMHO is not the enum..

if constexpr usage for variable length element Get<>

I am trying to get the second element of the list, but I get an error:
||=== Build: Debug in hellocpp17 (compiler: GNU GCC Compiler) ===|
/home/idf/Documents/c++/hellocpp17/main.cpp||In function ‘int main()’:|
/home/idf/Documents/c++/hellocpp17/main.cpp|67|error: no matching function for call to ‘Get<2>::Get(std::__cxx11::list<unsigned int>&)’|
/home/idf/Documents/c++/hellocpp17/main.cpp|40|note: candidate: constexpr Get<2>::Get()|
/home/idf/Documents/c++/hellocpp17/main.cpp|40|note: candidate expects 0 arguments, 1 provided|
/home/idf/Documents/c++/hellocpp17/main.cpp|40|note: candidate: constexpr Get<2>::Get(const Get<2>&)|
/home/idf/Documents/c++/hellocpp17/main.cpp|40|note: no known conversion for argument 1 from ‘std::__cxx11::list<unsigned int>’ to ‘const Get<2>&’|
/home/idf/Documents/c++/hellocpp17/main.cpp|40|note: candidate: constexpr Get<2>::Get(Get<2>&&)|
/home/idf/Documents/c++/hellocpp17/main.cpp|40|note: no known conversion for argument 1 from ‘std::__cxx11::list<unsigned int>’ to ‘Get<2>&&’|
||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|
Program:
#include <iostream>
#include <algorithm>
#include <list>
using namespace std;
template<unsigned n>
struct Get
{
template<class X, class...Xs>
constexpr auto operator()(X x, Xs...xs)
{
if constexpr(n > sizeof...(xs) )
{
return;
}
else if constexpr(n > 0)
{
return Get<n-1> {}(xs...);
}
else
{
return x;
}
}
};
int main()
{
list<unsigned> l = { 7, 5, 16, 8 };
unsigned l2 = Get<2>(l);
cout << l2 << endl;
return 0;
}
EDIT 1
If I instantiate a Get<2>, this error is reported by the compiler
unsigned l2 = Get<2>()(l);
/home/idf/Documents/c++/hellocpp17/main.cpp|67|error: void value not ignored as it ought to be|
You can try with
unsigned l2 = Get<2>{}(7, 5, 16, 8);
The first problem in your code is that
Get<2>(l);
isn't a call to operator() of Get<2>; it's a construction of a Get<2> object with a std::list parameter.
Unfortunately there isn't a Get<2> constructor that receive a std::list.
The second problem in your code is that if you call the operator() of Get<2> as you think
Get<2>{}(l)
where l is a std::list, you pass a single argument; not a variadic list of arguments. And you can use the list l only run-time, not compile time as you want.
Unfortunately the Get<2>{}(7, 5, 16, 8) way (the operator() that receive a variadic list of arguments) is incompatible with a variable that contain a list.
I mean... you can't do something as follows
auto l = something{7, 5, 16, 8};
Get<2>{}(l);
But, if you modify the operator() to receive a std::integer_sequence as follows
template <template <typename X, X...> class C,
typename T, T I0, T ... Is>
constexpr auto operator() (C<T, I0, Is...> const &)
{
if constexpr (n > sizeof...(Is) )
return;
else if constexpr (n > 0)
return Get<n-1>{}(C<T, Is...>{});
else
return I0;
}
you can pass through a l variable as follows
auto l { std::integer_sequence<int, 7, 5, 16, 8>{} };
unsigned l2 = Get<2>{}(l);

Simple constexpr function failed to compile with GCC (clang is OK)

The following code does not compile with GCC 5.2 (C++14). It does compile with clang 3.6 (C++14). (original code can be found here)
#include <cstddef>
#include <algorithm>
#include <type_traits>
#include <utility>
template <typename T>
class aggregate_wrapper;
template <typename T, std::size_t n>
class aggregate_wrapper<T[n]> {
public:
using array = T[n];
template <typename... Ts, typename = decltype(array{std::declval<Ts>()...})>
aggregate_wrapper(Ts&&... xs)
: arr_{std::forward<Ts>(xs)...} {
// nop
}
aggregate_wrapper(const array& arr) {
std::copy(arr, arr + n, arr_);
}
aggregate_wrapper(array&& arr) {
std::move(arr, arr + n, arr_);
}
operator T* () {
return arr_;
}
operator const T* () const {
return arr_;
}
constexpr std::size_t size() const {
return n;
}
private:
array arr_;
};
int main() {
aggregate_wrapper<int[3]> arr;
static_assert(arr.size() == 3, "");
}
The error message produced is
main.cpp: In function 'int main()':
main.cpp:44:3: error: non-constant condition for static assertion
static_assert(arr.size() == 3, "");
^
main.cpp:44:25: error: call to non-constexpr function 'constexpr std::size_t aggregate_wrapper<T [n]>::size() const [with T = int; long unsigned int n = 3ul; std::size_t = long unsigned int]'
static_assert(arr.size() == 3, "");
^
main.cpp:34:25: note: 'constexpr std::size_t aggregate_wrapper<T [n]>::size() const [with T = int; long unsigned int n = 3ul; std::size_t = long unsigned int]' is not usable as a constexpr function because:
constexpr std::size_t size() const {
^
main.cpp:34:25: error: enclosing class of constexpr non-static member function 'constexpr std::size_t aggregate_wrapper<T [n]>::size() const [with T = int; long unsigned int n = 3ul; std::size_t = long unsigned int]' is not a literal type
main.cpp:10:7: note: 'aggregate_wrapper<int [3]>' is not literal because:
class aggregate_wrapper<T[n]> {
^
main.cpp:10:7: note: 'aggregate_wrapper<int [3]>' is not an aggregate, does not have a trivial default constructor, and has no constexpr constructor that is not a copy or move constructor
Any ideas? Should the code compile according to the standard?
Or you could just make your existing variadic constructor serve as your constexpr constructor to perform the default construction:
template <typename... Ts, typename = decltype(array{std::declval<Ts>()...})>
constexpr // <---- ADD THIS
aggregate_wrapper(Ts&&... xs)
: arr_{std::forward<Ts>(xs)...} {
// nop
}
In order for g++ to get it compiled you will need to add a default constructor:
aggregate_wrapper() = default;
please see it in action at: http://coliru.stacked-crooked.com/a/df1ac057960bebc7
I have the feeling that clang under the hood added it, but I am not 100% sure ...
GCC is wrong. Its diagnostic, in part, says:
main.cpp:34:25: note: '<...>' is not usable as a constexpr function because:
main.cpp:34:25: error: enclosing class of constexpr non-static member function '<...>' is not a literal type
... but there is no such rule. See [dcl.constexpr]/3 for the list of constraints that apply here.
You can work around the bogus GCC diagnostic by adding a dummy constexpr constructor (it's fine for that constructor to be private and/or deleted if you don't want any of your real constructors to be constexpr) or by making size be static.

Construct type over enum and forward different number of arguments

Let's say I have several types bound into a variant.
On another side, I have an enum on which some of the previous types can be deduced from, so I can have a runtime pseudo factory:
#include <boost/variant.hpp>
enum class Type { W, X, Y, Z };
struct A {};
struct B
{
B(int) {}
};
struct C
{
C(int, int) {}
};
using variant_t = boost::variant<A, B, C>;
template<typename... Args>
variant_t MakeVariantOverEnum(Type type, Args&&... args)
{
switch (type)
{
case Type::X: return B(std::forward<Args>(args)...); break;
case Type::Z: return C(std::forward<Args>(args)...); break;
default: return A(std::forward<Args>(args)...); break;
}
}
// meant to be fully runtime
const Type GetTypeFromIO() { return Type::Z; }
const int GetFirstArgFromIO() { return 0; }
const int GetSecondArgFromIO() { return 0; }
int main()
{
const Type type = GetTypeFromIO();
const int firstArg = GetFirstArgFromIO();
const int secondArg = GetSecondArgFromIO();
variant_t newVariant;
if (firstArg != 0 && secondArg != 0) newVariant = MakeVariantOverEnum(type, firstArg, secondArg);
else if (firstArg != 0) newVariant = MakeVariantOverEnum(type, firstArg);
else newVariant = MakeVariantOverEnum(type);
}
2 things bother me in this code:
** How can I have only 1 call to MakeVariantOverEnum by passing all the arguments and then discarding those 'invalid' cases ( ==0 in my sample)? Can I do it inside the MakeVariantOverEnum with some SFINAE mechanism?
** It doesn't compile because the compiler tries to match all the constructors with all the arguments:
main.cpp: In instantiation of 'variant_t MakeVariantOverEnum(Type, Args&& ...) [with Args = {const int&, const int&}; variant_t = boost::variant<A, B, C>]':
main.cpp:44:100: required from here
main.cpp:24:59: error: no matching function for call to 'B::B(const int&, const int&)'
case Type::X: return B(std::forward<Args>(args)...); break;
^
main.cpp:24:59: note: candidates are:
main.cpp:9:2: note: B::B(int)
B(int) {}
^
main.cpp:9:2: note: candidate expects 1 argument, 2 provided
main.cpp:7:8: note: constexpr B::B(const B&)
struct B
^
main.cpp:7:8: note: candidate expects 1 argument, 2 provided
main.cpp:7:8: note: constexpr B::B(B&&)
main.cpp:7:8: note: candidate expects 1 argument, 2 provided
And so on for the other types...
So my question is: how can I make it work at this point?
Thanks!
PS: code is live here => http://coliru.stacked-crooked.com/a/4bc1e326be27b3dd
Easiest approach for your problem:
enum class Type { W, X, Y, Z };
struct A {};
struct B
{
B(int) {}
};
struct C
{
C(int, int) {}
};
using variant_t = boost::variant<A, B, C>;
variant_t MakeVariantOverEnum(Type type, int param1, int param2)
{
switch (type)
{
case Type::X: return B(param1);
case Type::Z: return C(param1, param2);
default: return A();
}
}
// meant to be fully runtime
const Type GetTypeFromIO() { return Type::Z; }
const int GetFirstArgFromIO() { return 0; }
const int GetSecondArgFromIO() { return 0; }
int main()
{
const Type type = GetTypeFromIO();
const int firstArg = GetFirstArgFromIO();
const int secondArg = GetSecondArgFromIO();
variant_t newVariant = MakeVariantOverEnum(type, firstArg, secondArg);
}
The variadic template just makes things more complicated and will not help you in any way.
If all your structs use the same parameters (at least: if the parameter is used, the same type is used everywhere) this will work. However if you have different parameter types for different objects, you need to consider if it wouldn't be easier to just do the switch in the main function itself (and then get the correct parameters):
Assume the following expansion:
struct D
{
D(float) {}
}
So now you suddenly have a situation where your const int firstArg would be a float... (that will not work for B and C however)
Alternatively you specify a third parameter float param3 and only use this parameter when creating a type. (but then what about param1 and param2?)
Note: It still would be possible to use some factory pattern, but that would be quite a bit more complex (each object needs its own factory which will get the correct parameters and then create the correct struct...)