Can I use SFINAE to compare enum class Value? - c++

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.

Related

C++ constructor behave differently according to input, with help of template

I'm trying to use the template to initialize an object. The code should assign s according to different parameters input, specifically "type". However, I can't get this to work.
#include <iostream>
#include <cmath>
using namespace std;
// different discount
class Strategy {
public:
virtual double GetResult(double) = 0;
};
class SellNormal: public Strategy {
public:
double GetResult(double original) override {
return original;
}
};
class SellDiscount: public Strategy {
double rate;
public:
SellDiscount(double r){
rate = r;
}
double GetResult(double original) override {
return original * rate;
}
};
class SellReturn: public Strategy {
int fulfill;
int reduce;
public:
SellReturn(int f, int r): fulfill(f), reduce(r){}
double GetResult(double original) override {
return original - (int(original) / fulfill) * reduce;
}
};
class SellContext{
Strategy* s;
public:
enum type{
NORMAL,
DISCOUNT,
RETURN
};
template<typename ...Ts>
SellContext(type t, Ts... args){
switch(t){
case NORMAL:
s = new SellNormal();
break;
case DISCOUNT:
// double
s = new SellDiscount(args...);
break;
case RETURN:
// int, int
s = new SellReturn(args...);
break;
}
}
double getResult(double original){
return s->GetResult(original);
}
};
int main(){
auto c1 = new SellContext(SellContext::type::NORMAL);
auto c2 = new SellContext(SellContext::type::DISCOUNT, 0.8);
auto c3 = new SellContext(SellContext::type::RETURN, 300, 100);
cout << c1->getResult(1000);
cout << c2->getResult(1000);
cout << c3->getResult(1000);
}
It's telling me that C++ can't find the appropriate constructor. How should I deal with this elegantly? I know I could achieve this by overloading constructors with different parameters. But isn't that too verbose?
The errors are as below
s
trategy_pattern_with_simple_factory.cpp: In instantiation of 'SellContext::SellContext(SellContext::type, Ts ...) [with Ts = {}]':
strategy_pattern_with_simple_factory.cpp:71:56: required from here
strategy_pattern_with_simple_factory.cpp:57:17: error: no matching function for call to 'SellDiscount::SellDiscount()'
s = new SellDiscount(args...);
^~~~~~~~~~~~~~~~~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:23:5: note: candidate: 'SellDiscount::SellDiscount(double)'
SellDiscount(double r){
^~~~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:23:5: note: candidate expects 1 argument, 0 provided
strategy_pattern_with_simple_factory.cpp:20:7: note: candidate: 'constexpr SellDiscount::SellDiscount(const SellDiscount&)'
class SellDiscount: public Strategy {
^~~~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:20:7: note: candidate expects 1 argument, 0 provided
strategy_pattern_with_simple_factory.cpp:20:7: note: candidate: 'constexpr SellDiscount::SellDiscount(SellDiscount&&)'
strategy_pattern_with_simple_factory.cpp:20:7: note: candidate expects 1 argument, 0 provided
strategy_pattern_with_simple_factory.cpp:61:17: error: no matching function for call to 'SellReturn::SellReturn()'
s = new SellReturn(args...);
^~~~~~~~~~~~~~~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:35:5: note: candidate: 'SellReturn::SellReturn(int, int)'
SellReturn(int f, int r): fulfill(f), reduce(r){}
^~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:35:5: note: candidate expects 2 arguments, 0 provided
strategy_pattern_with_simple_factory.cpp:31:7: note: candidate: 'constexpr SellReturn::SellReturn(const SellReturn&)'
class SellReturn: public Strategy {
^~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:31:7: note: candidate expects 1 argument, 0 provided
strategy_pattern_with_simple_factory.cpp:31:7: note: candidate: 'constexpr SellReturn::SellReturn(SellReturn&&)'
strategy_pattern_with_simple_factory.cpp:31:7: note: candidate expects 1 argument, 0 provided
strategy_pattern_with_simple_factory.cpp: In instantiation of 'SellContext::SellContext(SellContext::type, Ts ...) [with Ts = {double}]':
strategy_pattern_with_simple_factory.cpp:72:63: required from here
strategy_pattern_with_simple_factory.cpp:61:17: error: no matching function for call to 'SellReturn::SellReturn(double&)'
s = new SellReturn(args...);
^~~~~~~~~~~~~~~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:35:5: note: candidate: 'SellReturn::SellReturn(int, int)'
SellReturn(int f, int r): fulfill(f), reduce(r){}
^~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:35:5: note: candidate expects 2 arguments, 1 provided
strategy_pattern_with_simple_factory.cpp:31:7: note: candidate: 'constexpr SellReturn::SellReturn(const SellReturn&)'
class SellReturn: public Strategy {
^~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:31:7: note: no known conversion for argument 1 from 'double' to 'const SellReturn&'
strategy_pattern_with_simple_factory.cpp:31:7: note: candidate: 'constexpr SellReturn::SellReturn(SellReturn&&)'
strategy_pattern_with_simple_factory.cpp:31:7: note: no known conversion for argument 1 from 'double' to 'SellReturn&&'
strategy_pattern_with_simple_factory.cpp: In instantiation of 'SellContext::SellContext(SellContext::type, Ts ...) [with Ts = {int, int}]':
strategy_pattern_with_simple_factory.cpp:73:66: required from here
strategy_pattern_with_simple_factory.cpp:57:17: error: no matching function for call to 'SellDiscount::SellDiscount(int&, int&)'
s = new SellDiscount(args...);
^~~~~~~~~~~~~~~~~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:23:5: note: candidate: 'SellDiscount::SellDiscount(double)'
SellDiscount(double r){
^~~~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:23:5: note: candidate expects 1 argument, 2 provided
strategy_pattern_with_simple_factory.cpp:20:7: note: candidate: 'constexpr SellDiscount::SellDiscount(const SellDiscount&)'
class SellDiscount: public Strategy {
^~~~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:20:7: note: candidate expects 1 argument, 2 provided
strategy_pattern_with_simple_factory.cpp:20:7: note: candidate: 'constexpr SellDiscount::SellDiscount(SellDiscount&&)'
strategy_pattern_with_simple_factory.cpp:20:7: note: candidate expects 1 argument, 2 provided
First, Strategy must have a virtual destructor since you'll be destroying instances via base class pointers.
It looks like you are trying to use type as some sort of tag and you can't explicitly supply template parameters to the constructor. That would be a template parameter to the class itself - and SellContext is not a class template, so using tags is a good idea. They do need to be of different types though, so I suggest creating separate tag types and also to make the pointer into a smart pointer.
Example:
#include <memory>
class SellContext{
std::unique_ptr<Strategy> s; // <- smart pointer
public:
// tag types and tag instances:
static constexpr struct NORMAL {} normal_tag{};
static constexpr struct DISCOUNT {} discount_tag{};
static constexpr struct RETURN {} return_tag{};
// The constructors you need. No `switch` needed:
template<class... Args>
SellContext(NORMAL, Args&&... args) :
s(std::make_unique<SellNormal>(std::forward<Args>(args)...)) {}
template<class... Args>
SellContext(DISCOUNT, Args&&... args) :
s(std::make_unique<SellDiscount>(std::forward<Args>(args)...)) {}
template<class... Args>
SellContext(RETURN, Args&&... args) :
s(std::make_unique<SellReturn>(std::forward<Args>(args)...)) {}
double getResult(double original){
return s->GetResult(original);
}
};
Using them would then be done like this:
int main(){
auto c1 = std::make_unique<SellContext>(SellContext::normal_tag);
auto c2 = std::make_unique<SellContext>(SellContext::discount_tag, 0.8);
auto c3 = std::make_unique<SellContext>(SellContext::return_tag, 300, 100);
std::cout << c1->getResult(1000) << '\n';
}
Demo
Using constexpr-if:
#include <type_traits>
class SellContext{
std::unique_ptr<Strategy> s;
public:
static constexpr struct NORMAL {} normal_tag{};
static constexpr struct DISCOUNT {} discount_tag{};
static constexpr struct RETURN {} return_tag{};
template<class Tag, class... Args>
SellContext(Tag, Args&&... args) {
static_assert(std::is_same_v<NORMAL, Tag> ||
std::is_same_v<DISCOUNT, Tag> ||
std::is_same_v<RETURN, Tag>);
if constexpr(std::is_same_v<NORMAL, Tag>)
s = std::make_unique<SellNormal>(std::forward<Args>(args)...);
if constexpr(std::is_same_v<DISCOUNT, Tag>)
s = std::make_unique<SellDiscount>(std::forward<Args>(args)...);
if constexpr(std::is_same_v<RETURN, Tag>)
s = std::make_unique<SellReturn>(std::forward<Args>(args)...);
}
double getResult(double original){
return s->GetResult(original);
}
};
Demo
Since the current constructors of the Strategy objects are all different, you could make it even simpler and remove the tags:
class SellContext{
std::unique_ptr<Strategy> s;
public:
SellContext() : s(std::make_unique<SellNormal>()) {}
SellContext(double x) : s(std::make_unique<SellDiscount>(x)) {}
SellContext(double x, double y) : s(std::make_unique<SellReturn>(x, y)) {}
double getResult(double original){
return s->GetResult(original);
}
};
int main(){
auto c1 = std::make_unique<SellContext>();
auto c2 = std::make_unique<SellContext>(0.8);
auto c3 = std::make_unique<SellContext>(300, 100);
std::cout << c1->getResult(1000) << '\n';
}
Demo

Callback function in a templated function accepting variable argument type using `switch` statement

I am trying to call a callback function from within a templated function. But the arguments for the callback function depend on switch statement.
Here's the working code which explains what I intend to do using a toy example.
#include <vector>
struct A {};
struct B {};
struct C {};
struct D {};
enum class StructType
{
A,
B,
C,
D
};
std::vector<A> vec_A;
std::vector<B> vec_B;
std::vector<C> vec_C;
std::vector<D> vec_D;
template <template <class> class Container, class ValueType>
void process(const StructType& struct_type)
{
auto callback = [&](Container<ValueType>& v) {};
switch(struct_type)
{
case StructType::A:
callback(vec_A);
break;
case StructType::B:
callback(vec_B);
break;
case StructType::C:
callback(vec_C);
break;
case StructType::D:
callback(vec_D);
break;
}
}
int main()
{
process<std::vector, A>(StructType::A);
}
On compilation, I get the following error:
$ g++ template.cpp
template.cpp: In instantiation of ‘void process(const StructType&) [with Iterator = std::vector; ValueType = A]’:
template.cpp:47:26: required from here
template.cpp:33:15: error: no match for call to ‘(process<std::vector, A>(const StructType&)::<lambda(std::vector<A>&)>) (std::vector<B>&)’
33 | callback(vec_B);
| ~~~~~~~~^~~~~~~
template.cpp:25:19: note: candidate: ‘process<std::vector, A>(const StructType&)::<lambda(std::vector<A>&)>’
25 | auto callback = [&](Iterator<ValueType>& v) {};
| ^
template.cpp:25:19: note: no known conversion for argument 1 from ‘std::vector<B>’ to ‘std::vector<A>&’
template.cpp:36:15: error: no match for call to ‘(process<std::vector, A>(const StructType&)::<lambda(std::vector<A>&)>) (std::vector<C>&)’
36 | callback(vec_C);
| ~~~~~~~~^~~~~~~
template.cpp:25:19: note: candidate: ‘process<std::vector, A>(const StructType&)::<lambda(std::vector<A>&)>’
25 | auto callback = [&](Iterator<ValueType>& v) {};
| ^
template.cpp:25:19: note: no known conversion for argument 1 from ‘std::vector<C>’ to ‘std::vector<A>&’
template.cpp:39:15: error: no match for call to ‘(process<std::vector, A>(const StructType&)::<lambda(std::vector<A>&)>) (std::vector<D>&)’
39 | callback(vec_D);
| ~~~~~~~~^~~~~~~
template.cpp:25:19: note: candidate: ‘process<std::vector, A>(const StructType&)::<lambda(std::vector<A>&)>’
25 | auto callback = [&](Iterator<ValueType>& v) {};
| ^
template.cpp:25:19: note: no known conversion for argument 1 from ‘std::vector<D>’ to ‘std::vector<A>&’
My g++ version is: 11.1.0.
I can understand the error but just not sure how to fix this.
Any help is greatly appreciated. Thanks.
EDIT 1: Iterator -> Container for template type made more sense as suggested by #Ted Lyngmo.
As your errors say, there are no matches for callback functors you haven't created.
I suggest removing the runtime argument to process and using the information you have in the function template parameters only.
Example:
#include <vector>
#include <type_traits>
struct A {};
struct B {};
struct C {};
struct D {};
// enum class StructType {A, B, C, D }; // perhaps not needed anymore
std::vector<A> vec_A;
std::vector<B> vec_B;
std::vector<C> vec_C;
std::vector<D> vec_D;
template<class T> auto& get_vec();
template<> auto& get_vec<std::vector<A>>() { return vec_A; }
template<> auto& get_vec<std::vector<B>>() { return vec_B; }
template<> auto& get_vec<std::vector<C>>() { return vec_C; }
template<> auto& get_vec<std::vector<D>>() { return vec_D; }
template <template <class, class...> class Container, class ValueType>
void process() {
using container_type = Container<ValueType>;
auto callback = [&](container_type&) {};
callback(get_vec<container_type>());
}
int main() {
process<std::vector, A>();
}

Variadic template and default value with inheritance

#include <iostream>
struct A {
int a;
std::string b;
A(int a_, std::string b_) : a(a_), b(b_) { std::cout << a << b << std::endl; }
};
struct B : public A {
static const int VERSION=2;
float c;
template<typename ... ARGS>
B(float c_, int v=VERSION, ARGS&&... args) : A(v, std::forward<ARGS>(args)...), c(c_) { std::cout << c << std::endl; }
};
int main() {
B(3.14, "abc");
}
Hi all, the compiler gives gives me template argument deduction/substitution failed error. How can I use a default value with a variadic template?
variadic.cpp: In function ‘int main()’:
variadic.cpp:18:15: error: no matching function for call to ‘B::B(double, const char [4])’
B(3.14, "abc");
^
variadic.cpp:14:2: note: candidate: template<class ... ARGS> B::B(float, int, ARGS&& ...)
B(float c_, int v=VERSION, ARGS&&... args) : A(v, std::forward<ARGS>(args)...), c(c_) { std::cout << c << std::endl; }
^
variadic.cpp:14:2: note: template argument deduction/substitution failed:
variadic.cpp:18:15: note: cannot convert ‘"abc"’ (type ‘const char [4]’) to type ‘int’
B(3.14, "abc");
^
variadic.cpp:9:8: note: candidate: B::B(const B&)
struct B : public A {
^
variadic.cpp:9:8: note: candidate expects 1 argument, 2 provided
variadic.cpp:9:8: note: candidate: B::B(B&&)
variadic.cpp:9:8: note: candidate expects 1 argument, 2 provided
The problem here is that your constructor can be called with one, two or more arguments.
If you call it with one argument, the second argument is defaulted.
If you provide two or more arguments, the provided default argument is not used. Your second argument is used, and it has to match the type of the second parameter.
Note that in general, you can achieve similar results by overloading a function instead of providing default arguments. In this case, I suspect that would give you the results you intend, but that's me guessing at your intents.
The problem is if a function/method parameter has a default value, all following parameters must have a default value.
So
template<typename ... ARGS>
B(float c_, int v=VERSION, ARGS&&... args)
: A(v, std::forward<ARGS>(args)...), c(c_)
{ std::cout << c << std::endl; }
is wrong because there aren't default values for args.
Or better: you can write the following signature
B(float c_, int v=VERSION, ARGS&&... args)
but the default value for v is used only if you pass to the constructor only a value (c_) because args... is empty so v is the last parameter.
But if you want a some args..., the default value for v is ignored because, otherwise, the compiler can't know if a second integer parameter is the not-default value for v or the first args...

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...)

Variadic template argument deduction fails when passing initializer lists

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.