Deducing parameters from initializer list - c++

So here is the scenario I have been to trying to work upon
#include <iostream>
#include <vector>
#include <string>
#include <complex>
class Double
{
private:
double dd;
std::vector<double> gg;
std::string ss;
public:
Double(double d,const std::string& s = std::string())
{
dd = d;
ss = s;
gg = std::vector<double>();
}
Double(const std::vector<double>& d,const std::string& s = std::string())
{
dd = 0;
ss = s;
gg = d;
}
double getdd() const { return dd; }
};
template<typename T>
Double getDouble(T t,const std::string& s = std::string())
{
return Double(t,s);
}
std::string to_string(Double d)
{
double zd = d.getdd() + 1.0;
return std::to_string(zd);
}
class TEST
{
public:
template<typename T_0>
TEST(const T_0 & var_0)
{
auto x = to_string(getDouble(var_0));
}
};
int main()
{
TEST ds ({3.4,"ssd"});
std::cout << "Reached heare\n";
return 0;
}
Essentially if I call
Double dd ({3.4,"ssd"});
everything works fine but if I try the same thing with the class TEST, I get the following error.
main.cpp: In function 'int main()':
main.cpp:57:25: error: no matching function for call to 'TEST::TEST(<brace-enclosed initializer list>)'
TEST ds ({3.4,"ssd"});
^
main.cpp:48:5: note: candidate: template<class T_0> TEST::TEST(const T_0&)
TEST(const T_0 & var_0)
^
main.cpp:48:5: note: template argument deduction/substitution failed:
main.cpp:57:25: note: couldn't deduce template parameter 'T_0'
TEST ds ({3.4,"ssd"});
^
main.cpp:44:8: note: candidate: constexpr TEST::TEST(const TEST&)
class TEST
^
main.cpp:44:8: note: no known conversion for argument 1 from '<brace-enclosed initializer list>' to 'const TEST&'
main.cpp:44:8: note: candidate: constexpr TEST::TEST(TEST&&)
main.cpp:44:8: note: no known conversion for argument 1 from '<brace-enclosed initializer list>' to 'TEST&&'
I have tried to deduce the parameters adding a constructor as below
Double (std::initializer_list<boost::any> xx)
but it hasnt worked for me.
Is there any way to get past this issue ? I cant use the explicit type at the callsite since the elements of the initializer list are not of the same type.

Unfortunately the braced-init-list doesn't have any type; the template parameter T_0 can't be deduced from it.
You can use parameter pack instead.
template<typename... T_0>
TEST(const T_0 & ... var_0)
{
auto x = to_string(getDouble(var_0...));
}
then use it like
TEST ds (3.4,"ssd");
LIVE

This call works
Double dd ({3.4,"ssd"});
because there is appropriate constructor
Double(double d,const std::string& s = std::string())
{
dd = d;
ss = s;
gg = std::vector<double>();
}
This call
TEST ds ({3.4,"ssd"});
does not work because the template constructor has only one parameter
template<typename T_0>
TEST(const T_0 & var_0)
{
auto x = to_string(getDouble(var_0));
}
Pay attention to that this construction {3.4,"ssd"} does not have a type. SO the compiler reports that
main.cpp:48:5: note: template argument deduction/substitution failed:
main.cpp:57:25: note: couldn't deduce template parameter 'T_0'
TEST ds ({3.4,"ssd"});
^

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

How to static_cast a pointer to const member function?

Surprisingly (embarrassingly?) I cannot get the syntax of the static_const of a const member function right. In short (details below) if the member function is not marked const I use:
static_cast<std::vector<double> (mymodule::Foo::*)(const std::vector<double>&)>(&mymodule::Foo::bar)
but marking the member function Foo::bar(...) const the compiler does not know what to do:
error: address of overloaded function 'bar' cannot be static_cast to type 'std::vector<double> (mymodule::Foo::*)(const std::vector<double> &)'
Where should I put the function's constness?
Details
I'm trying to create Python binding for the following module:
namespace mymodule {
class Foo
{
public:
Foo() = default;
template <class T>
T bar(const T& a) const
{
T ret = a;
for (auto& i : ret) {
i *= 2.0;
}
return ret;
}
template <class T>
T bar(const T& a, double f) const
{
T ret = a;
for (auto& i : ret) {
i *= f;
}
return ret;
}
};
} // namespace mymodule
whereby I write the Python bindings with pybind11:
#include <pybind11/pybind11.h>
namespace py = pybind11;
PYBIND11_MODULE(example, m)
{
py::class_<mymodule::Foo>(m, "Foo")
.def(py::init<>())
.def("bar",
static_cast<std::vector<double> (mymodule::Foo::*)(const std::vector<double>&)>(&mymodule::Foo::bar),
py::arg("a"))
.def("bar",
static_cast<std::vector<double> (mymodule::Foo::*)(const std::vector<double>&, double)>(&mymodule::Foo::bar),
py::arg("a"),
py::arg("f"));
}
which fails to compile:
.../example.cpp:54:14: error: address of overloaded function 'bar' cannot be static_cast to type 'std::vector<double> (mymodule::Foo::*)(const std::vector<double> &)'
static_cast<std::vector<double> (mymodule::Foo::*)(const std::vector<double>&)>(&mymodule::Foo::bar),
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.../example.cpp:19:7: note: candidate function template
T bar(const T& a) const
^
.../example.cpp:29:7: note: candidate function template
T bar(const T& a, double f) const
^
.../example.cpp:58:14: error: address of overloaded function 'bar' cannot be static_cast to type 'std::vector<double> (mymodule::Foo::*)(const std::vector<double> &, double)'
static_cast<std::vector<double> (mymodule::Foo::*)(const std::vector<double>&, double)>(&mymodule::Foo::bar),
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.../example.cpp:19:7: note: candidate function template
T bar(const T& a) const
^
.../example.cpp:29:7: note: candidate function template
T bar(const T& a, double f) const
^
2 errors generated.
You should add const at last as:
static_cast<std::vector<double> (mymodule::Foo::*)(const std::vector<double>&) const>(&mymodule::Foo::bar),
// ^^^^^

c++17 : lambda to std::function conversion failure

I'm currently exploring c++17 additions. After playing around with std::variant, wanted to use std::optional too, with the same example. Currently seeing that the compilation fails because of the following error:
error: no viable conversion from returned value of type
'(lambda at ./html_parser.hpp:53:9)' to function return type 'Parser<char>' (aka
'std::__1::function<std::__1::optional<std::__1::pair<char, std::__1::basic_string<char> > >
(std::__1::basic_string<char>)>')
return [=](std::string& input) -> ParserResult<char> {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/acid/tools/include/c++/v1/functional:1627:5: note: candidate constructor not viable: no known conversion
from '(lambda at ./html_parser.hpp:53:9)' to 'std::nullptr_t' (aka 'nullptr_t') for 1st argument
function(nullptr_t) _NOEXCEPT : __f_(0) {}
^
/home/acid/tools/include/c++/v1/functional:1628:5: note: candidate constructor not viable: no known conversion
from '(lambda at ./html_parser.hpp:53:9)' to 'const
std::__1::function<std::__1::optional<std::__1::pair<char, std::__1::basic_string<char> > >
(std::__1::basic_string<char>)> &' for 1st argument
function(const function&);
^
/home/acid/tools/include/c++/v1/functional:1629:5: note: candidate constructor not viable: no known conversion
from '(lambda at ./html_parser.hpp:53:9)' to 'std::__1::function<std::__1::optional<std::__1::pair<char,
std::__1::basic_string<char> > > (std::__1::basic_string<char>)> &&' for 1st argument
function(function&&) _NOEXCEPT;
^
/home/acid/tools/include/c++/v1/functional:1631:5: note: candidate template ignored: requirement
'__callable<(lambda at ./html_parser.hpp:53:9)>::value' was not satisfied [with _Fp =
(lambda at ./html_parser.hpp:53:9)]
function(_Fp);
^
1 error generated.
To parse the HTML to give the DOM, started with declaring some parser combinators as follows:
#pragma once
#include <string>
#include <utility>
#include <functional>
#include <optional>
namespace dragon {
namespace html {
namespace parser {
template <typename ParserOutput, typename ParserInput = std::string>
using ParserResult = std::optional<std::pair<ParserOutput, ParserInput>>;
template<typename ParserOutput, typename ParserInput = std::string>
using Parser = std::function<ParserResult<ParserOutput, ParserInput>(ParserInput)>;
template <typename ParserOutput, typename ParserInput = std::string>
auto parse(Parser<ParserOutput, ParserInput> p, ParserInput i) -> ParserResult<ParserOutput, ParserInput>{
return p(i);
}
// few parser combinators.
// thenP combinator: applies the first parser, if it succeeds apply the second to the rest of
// the input left over by the first parser.
// currently just fails and returns empty!! does not provide any debugging info/msg
// as to why the parsing failed.
template<typename FirstParser, typename SecondParser>
auto thenP(FirstParser f, SecondParser s) {
return [=](std::string input) -> decltype(parse(s, std::string())) {
auto fv = parse(f, input);
if (fv) {
auto fvv = *fv;
return parse(s, fvv.second);
}
else {
return {};
}
};
}
template<typename FirstParser, typename SecondParser>
auto choiceP(FirstParser f, SecondParser s) {
return [=](std::string input) {
auto fv = parse(f, input);
if (!fv) return parse(s, input);
return fv;
};
}
auto charP(char match) -> Parser<char> {
return [=](std::string& input) -> ParserResult<char> {
if ((input.empty() == false) && (input[0] == match)) {
return std::make_pair(input[0], input.substr(1));
}
return {};
};
}
}
}
}
I'm seeing the above error, when trying to compile the simple use as shown below:
int main()
{
auto less = Parser::parser::charP('<');
auto greater = Parser::parser::charP('>');
auto lag = Parser::parser::thenP(less, greater);
auto log = Parser::parser::choiceP(less, greater);
auto lagv = lag("<>");
auto logv = log("|>");
return 0;
}
This compiles fine with Visual Studio 2017 (std=c++-latest). But Clang gives the above error. Trying to figure out the discrepancy between these two compilers. and how to fix this issue with Clang.
This is ill-formed:
auto charP(char match) -> Parser<char> {
return [=](std::string& input) -> ParserResult<char> { ... };
}
for the same reason that this is ill-formed:
std::function<void(int)> f = [](int& ){};
The lambda on the right is not invocable with an int, only an int&, so you can't construct a function<void(int)> out of it. MSVC has some permissive mode where it allows constructing a non-const lvalue reference from an rvalue, which is probably why it worked.

Pass a templatized type to a member function in C++

I'm trying to write a member function that can instantiate an object of a custom type (templatized), initializing its const& member to a local object of the function.
This is consistent since the lifetime of the custom type object is the same as the local_object.
The objective is caching some metadata of the local object because they don't change during its lifetime. The operator() (or any member function) computes some values, then used later in func, and the objective is offering a hook to change the behaviour of func.
Please no polymorphic solutions (currently used) due to (profiled) slowness.
This is a M(N)WE:
#include <vector>
class cls {
public:
template <typename Custom> int func() {
std::vector<int> local_object{0, 14, 32};
Custom c(local_object, 42);
return c();
}
};
template<typename AType> class One {
public:
One(const AType& obj, const int n): objref(obj), param(n), member_that_should_depend_on_objref(obj.size()) {}
int operator()() { return 42; }
private:
const AType& objref;
const int param;
float member_that_should_depend_on_objref;
};
template<typename AType> class Two {
public:
Two(const AType& obj, const int n): objref(obj), param(n), other_member_that_should_depend_on_objref(obj.empty()), other_dependent_member(obj.back()) {}
int operator()() { return 24; }
private:
const AType& objref;
const int param;
bool other_member_that_should_depend_on_objref;
int other_dependent_member;
};
int main() {
cls myobj;
auto a = myobj.func<One>();
auto b = (myobj.func<Two>)();
}
G++ 5.3.0 says
tmp.cpp: In function 'int main()':
tmp.cpp:34:30: error: no matching function for call to 'cls::func()'
auto a = myobj.func<One>();
^
tmp.cpp:4:36: note: candidate: template<class Custom> int cls::func()
template <typename Custom> int func() {
^
tmp.cpp:4:36: note: template argument deduction/substitution failed:
tmp.cpp:35:32: error: no matching function for call to 'cls::func()'
auto b = (myobj.func<Two>)();
^
tmp.cpp:4:36: note: candidate: template<class Custom> int cls::func()
template <typename Custom> int func() {
^
tmp.cpp:4:36: note: template argument deduction/substitution failed:
Clang++ 3.7.1 says:
tmp.cpp:34:20: error: no matching member function for call to 'func'
auto a = myobj.func<One>();
~~~~~~^~~~~~~~~
tmp.cpp:4:36: note: candidate template ignored: invalid explicitly-specified argument for template
parameter 'Custom'
template <typename Custom> int func() {
^
tmp.cpp:35:21: error: no matching member function for call to 'func'
auto b = (myobj.func<Two>)();
~~~~~~~^~~~~~~~~~
tmp.cpp:4:36: note: candidate template ignored: invalid explicitly-specified argument for template
parameter 'Custom'
template <typename Custom> int func() {
^
2 errors generated.
auto a = myobj.func<One>();
is wrong since One is a class template, not a class. Use
auto a = myobj.func<One<SomeType>>();
It's not clear from your code what SomeType should be.
Update
If you want to use:
auto a = myobj.func<One>();
you need to change func to use a template template parameter:
class cls {
public:
template <template <class> class Custom > int func() {
std::vector<int> local_object{0, 14, 32};
Custom<std::vector<int>> c(local_object, 42);
return c();
}
};
Perhaps that was your intention.

boost optional and user-defined conversion

I am unable to write a correct user defined conversion for a type Item. This is what I've tried:
#include <iostream>
#include <boost/optional.hpp>
struct A
{
int x;
};
struct Item
{
boost::optional<int> x_;
Item(){}
Item(const A& s)
: x_(s.x)
{
}
operator boost::optional<A>() const {
boost::optional<A> s;
if (x_) {
s->x = *x_;
}
return s;
}
};
std::vector<A> getA(const std::vector<Item> &items) {
std::vector<A> a;
for (const auto &i : items) {
if (i.x_) {
a.push_back(*static_cast<boost::optional<A>>(i)); // <- this line causes error
}
}
return a;
}
That is how I use it:
int main() {
A a;
a.x = 3;
Item i(a);
auto v = getA({i});
return 0;
}
g++ -std=c++11 says:
In file included from /usr/include/boost/optional.hpp:15:0,
from test.cpp:2:
/usr/include/boost/optional/optional.hpp: In instantiation of ‘void boost::optional_detail::optional_base<T>::construct(const Expr&, const void*) [with Expr = Item; T = A]’:
/usr/include/boost/optional/optional.hpp:262:25: required from ‘boost::optional_detail::optional_base<T>::optional_base(const Expr&, const Expr*) [with Expr = Item; T = A]’
/usr/include/boost/optional/optional.hpp:559:78: required from ‘boost::optional<T>::optional(const Expr&) [with Expr = Item; T = A]’
test.cpp:30:55: required from here
/usr/include/boost/optional/optional.hpp:392:8: error: no matching function for call to ‘A::A(const Item&)’
new (m_storage.address()) internal_type(expr) ;
^
/usr/include/boost/optional/optional.hpp:392:8: note: candidates are:
test.cpp:3:8: note: A::A()
struct A
^
test.cpp:3:8: note: candidate expects 0 arguments, 1 provided
test.cpp:3:8: note: constexpr A::A(const A&)
test.cpp:3:8: note: no known conversion for argument 1 from ‘const Item’ to ‘const A&’
test.cpp:3:8: note: constexpr A::A(A&&)
test.cpp:3:8: note: no known conversion for argument 1 from ‘const Item’ to ‘A&&’
Why does it try to find A struct constructor instead of use user defined conversion operator?
You may point me directly to any position of the user-defined conversion page because I am unable to find any reason for this. For example,
User-defined conversion function is invoked on the second stage of the implicit conversion, which consists of zero or one converting constructor or zero or one user-defined conversion function.
in my opinion directly says that if no conversion constructor is defined then user-defined conversion function will be used. Am I wrong? And if yes, how can I implement user-defined conversion then without defining conversion cunstructor in struct A ?
You have two issues with your code. Your optional operator never initializes the boost::optional. If you don't do that, accessing members is undefined behavior. What you have to do is:
operator boost::optional<A>() const {
boost::optional<A> s;
if (x_) {
s = A{*x_};
}
return s;
}
The second issue is when you do:
static_cast<boost::optional<A>>(i);
That is equivalent to:
boost::optional<A> __tmp(i);
But it turns out that boost::optional has an explicit template constructor. That will be preferred to your conversion function. The error you're seeing is the compiling going down the path of this factory constructor, where Item is not such a factory.
You could simply use boost::optional<A> directly:
std::vector<A> getA(const std::vector<Item> &items) {
std::vector<A> a;
for (boost::optional<A> opt : items) {
if (opt) {
a.push_back(*opt);
}
}
return a;
}
Or, since the constructor template is explicit, you could use the conversion operator in a non-explicit context:
boost::optional<A> opt = i;
a.push_back(*opt);
This has the added benefit of also being easier to read.