I'm trying to implement a class which wraps an arbitrary type and a mutex. To access the wrapped data, one needs to pass a function object as parameter of the locked method. The wrapper class will then pass the wrapped data as parameter to this function object.
I'd like my wrapper class to work with const & non-const, so I tried the following
#include <mutex>
#include <string>
template<typename T, typename Mutex = std::mutex>
class Mutexed
{
private:
T m_data;
mutable Mutex m_mutex;
public:
using type = T;
using mutex_type = Mutex;
public:
explicit Mutexed() = default;
template<typename... Args>
explicit Mutexed(Args&&... args)
: m_data{std::forward<Args>(args)...}
{}
template<typename F>
auto locked(F&& f) -> decltype(std::forward<F>(f)(m_data)) {
std::lock_guard<Mutex> lock(m_mutex);
return std::forward<F>(f)(m_data);
}
template<typename F>
auto locked(F&& f) const -> decltype(std::forward<F>(f)(m_data)) {
std::lock_guard<Mutex> lock(m_mutex);
return std::forward<F>(f)(m_data);
}
};
int main()
{
Mutexed<std::string> str{"Foo"};
str.locked([](auto &s) { /* this doesn't compile */
s = "Bar";
});
str.locked([](std::string& s) { /* this compiles fine */
s = "Baz";
});
return 0;
}
The first locked call with the generic lambda fails to compile with the following error
/home/foo/tests/lamdba_auto_const/lambda_auto_const/main.cpp: In instantiation of ‘main()::<lambda(auto:1&)> [with auto:1 = const std::__cxx11::basic_string<char>]’:
/home/foo/tests/lamdba_auto_const/lambda_auto_const/main.cpp:30:60: required by substitution of ‘template<class F> decltype (forward<F>(f)(((const Mutexed<T, Mutex>*)this)->Mutexed<T, Mutex>::m_data)) Mutexed<T, Mutex>::locked(F&&) const [with F = main()::<lambda(auto:1&)>]’
/home/foo/tests/lamdba_auto_const/lambda_auto_const/main.cpp:42:6: required from here
/home/foo/tests/lamdba_auto_const/lambda_auto_const/main.cpp:41:11: error: passing ‘const std::__cxx11::basic_string<char>’ as ‘this’ argument discards qualifiers [-fpermissive]
s = "Bar";
^
In file included from /usr/include/c++/5/string:52:0,
from /usr/include/c++/5/stdexcept:39,
from /usr/include/c++/5/array:38,
from /usr/include/c++/5/tuple:39,
from /usr/include/c++/5/mutex:38,
from /home/foo/tests/lamdba_auto_const/lambda_auto_const/main.cpp:1:
/usr/include/c++/5/bits/basic_string.h:558:7: note: in call to ‘std::__cxx11::basic_string<_CharT, _Traits, _Alloc>& std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::operator=(const _CharT*) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’
operator=(const _CharT* __s)
^
But the second call with the std::string& parameter is fine.
Why is that ? And is there a way to make it work as expected while using a generic lambda ?
This is a problem fundamentally with what happens with SFINAE-unfriendly callables. For more reference, check out P0826.
The problem is, when you call this:
str.locked([](auto &s) { s = "Bar"; });
We have two overloads of locked and we have to try both. The non-const overload works fine. But the const one – even if it won't be selected by overload resolution anyway – still has to be instantiated (it's a generic lambda, so to figure out what decltype(std::forward<F>(f)(m_data)) might be you have to instantiate it) and that instantiation fails within the body of the lambda. The body is outside of the immediate context, so it's not a substitution failure – it's a hard error.
When you call this:
str.locked([](std::string& s) { s = "Bar"; });
We don't need to look at the body at all during the whole process of overload resolution – we can simply reject at the call site (since you can't pass a const string into a string&).
There's not really a solution to this problem in the language today – you basically have to add constraints on your lambda to ensure that the instantiation failure happens in the immediate context of substitution rather than in the body. Something like:
str.locked([](auto &s) -> void {
s = "Bar";
});
Note that we don't need to make this SFINAE-friendly - we just need to ensure that we can determine the return type without instantiating the body.
A more thorough language solution would have been to allow for "Deducing this" (see the section in the paper about this specific problem). But that won't be in C++20.
Related
Here's a sample code: http://coliru.stacked-crooked.com/a/5f630d2d65cd983e
#include <variant>
#include <functional>
template<class... Ts> struct overloads : Ts... { using Ts::operator()...; };
template<class... Ts> overloads(Ts &&...) -> overloads<std::remove_cvref_t<Ts>...>;
template<typename... Ts, typename... Fs>
constexpr inline auto transform(const std::variant<Ts...> &var, Fs &&... fs)
-> decltype(auto) { return std::visit(overloads{fs...}, var); }
template<typename... Ts, typename... Fs>
constexpr inline auto transform_by_ref(const std::variant<Ts...> &var, Fs &&... fs)
-> decltype(auto) { return std::visit(overloads{std::ref(fs)...}, var); }
int main()
{
transform(
std::variant<int, double>{1.0},
[](int) { return 1; },
[](double) { return 2; }); // fine
transform_by_ref(
std::variant<int, double>{1.0},
[](int) { return 1; },
[](double) { return 2; }); // compilation error
return 0;
}
Here, I have adopted the well-known overloads helper type to invoke std::visit() with multiple lambdas.
transform() copies function objects so I write a new function transform_by_ref() which utilizes std::reference_wrapper to prevent copying function objects.
Even though original lambdas are temporary objects, the lifetime is ensured at the end of execution of transform_by_ref() and I think lifetime should not be a problem here.
transform() works as expected but transform_by_ref() causes compilation error:
main.cpp: In instantiation of 'constexpr decltype(auto) transform_by_ref(const std::variant<_Types ...>&, Fs&& ...) [with Ts = {int, double}; Fs = {main()::<lambda(int)>, main()::<lambda(double)>}]':
main.cpp:18:21: required from here
main.cpp:13:42: error: no matching function for call to 'visit(overloads<std::reference_wrapper<main()::<lambda(int)> >, std::reference_wrapper<main()::<lambda(double)> > >, const std::variant<int, double>&)'
13 | -> decltype(auto) { return std::visit(overloads{std::ref(fs)...}, var); }
| ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from main.cpp:1:
/usr/local/include/c++/12.1.0/variant:1819:5: note: candidate: 'template<class _Visitor, class ... _Variants> constexpr std::__detail::__variant::__visit_result_t<_Visitor, _Variants ...> std::visit(_Visitor&&, _Variants&& ...)'
1819 | visit(_Visitor&& __visitor, _Variants&&... __variants)
| ^~~~~
/usr/local/include/c++/12.1.0/variant:1819:5: note: template argument deduction/substitution failed:
In file included from /usr/local/include/c++/12.1.0/variant:37:
/usr/local/include/c++/12.1.0/type_traits: In substitution of 'template<class _Fn, class ... _Args> using invoke_result_t = typename std::invoke_result::type [with _Fn = overloads<std::reference_wrapper<main()::<lambda(int)> >, std::reference_wrapper<main()::<lambda(double)> > >; _Args = {const int&}]':
/usr/local/include/c++/12.1.0/variant:1093:11: required by substitution of 'template<class _Visitor, class ... _Variants> using __visit_result_t = std::invoke_result_t<_Visitor, std::__detail::__variant::__get_t<0, _Variants, decltype (std::__detail::__variant::__as(declval<_Variants>())), typename std::variant_alternative<0, typename std::remove_reference<decltype (std::__detail::__variant::__as(declval<_Variants>()))>::type>::type>...> [with _Visitor = overloads<std::reference_wrapper<main()::<lambda(int)> >, std::reference_wrapper<main()::<lambda(double)> > >; _Variants = {const std::variant<int, double>&}]'
/usr/local/include/c++/12.1.0/variant:1819:5: required by substitution of 'template<class _Visitor, class ... _Variants> constexpr std::__detail::__variant::__visit_result_t<_Visitor, _Variants ...> std::visit(_Visitor&&, _Variants&& ...) [with _Visitor = overloads<std::reference_wrapper<main()::<lambda(int)> >, std::reference_wrapper<main()::<lambda(double)> > >; _Variants = {const std::variant<int, double>&}]'
main.cpp:13:42: required from 'constexpr decltype(auto) transform_by_ref(const std::variant<_Types ...>&, Fs&& ...) [with Ts = {int, double}; Fs = {main()::<lambda(int)>, main()::<lambda(double)>}]'
main.cpp:18:21: required from here
/usr/local/include/c++/12.1.0/type_traits:3034:11: error: no type named 'type' in 'struct std::invoke_result<overloads<std::reference_wrapper<main()::<lambda(int)> >, std::reference_wrapper<main()::<lambda(double)> > >, const int&>'
3034 | using invoke_result_t = typename invoke_result<_Fn, _Args...>::type;
| ^~~~~~~~~~~~~~~
main.cpp: In instantiation of 'constexpr decltype(auto) transform_by_ref(const std::variant<_Types ...>&, Fs&& ...) [with Ts = {int, double}; Fs = {main()::<lambda(int)>, main()::<lambda(double)>}]':
main.cpp:18:21: required from here
/usr/local/include/c++/12.1.0/variant:1859:5: note: candidate: 'template<class _Res, class _Visitor, class ... _Variants> constexpr _Res std::visit(_Visitor&&, _Variants&& ...)'
1859 | visit(_Visitor&& __visitor, _Variants&&... __variants)
| ^~~~~
/usr/local/include/c++/12.1.0/variant:1859:5: note: template argument deduction/substitution failed:
main.cpp:13:42: note: couldn't deduce template parameter '_Res'
13 | -> decltype(auto) { return std::visit(overloads{std::ref(fs)...}, var); }
| ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I think I can fix this by not using std::visit() and implement my own visit function anyway.
However, I want to know why this code does not work as expected.
Why does my transform_by_ref() cause compilation error and how to fix it without custom visit function implementation?
Each std::reference_wrapper has an operator() overload that can be called with any argument list that the referenced lambda would accept as arguments.
That means the reference wrappers for both [](int) { return 1; } and [](double) { return 2; } have operator() overloads that accept an int argument as well as an double argument, both without conversion of the argument.
So when std::visit tries to do overload resolution for a specific element type of the variant, the operator() overloads made visible via using Ts::operator()...; for both reference wrappers of the lambdas will be viable, but in contrast to the non-reference-wrapper case, both overloads will be viable without conversions of the argument, meaning that they are equally good and hence overload resolution ambiguous.
The ambiguity can be avoided by enforcing that the lambdas take only exactly the type they are supposed to match as argument (assuming C++20 here):
transform_by_ref(
std::variant<int, double>{1.0},
[](std::same_as<int> auto) { return 1; },
[](std::same_as<double> auto) { return 2; });
or by using a single overload with if constexpr in its body to branch on the type of the argument.
While it is possible to make the operator() of a wrapper class SFINAE-friendly so that it won't be considered viable if the wrapped callable isn't, it is impossible to "forward" the conversion rank of calls for calls to such a wrapper, at least in general. For non-generic lambdas specifically, it is theoretically possible to extract the parameter type in the wrapper and use it as the parameter type of the operator() overload, but that is messy and doesn't work with generic callables. Proper reflection would be required to implement such a wrapper.
In your code for transform you are using fs directly as lvalue instead of properly forwarding its value category via std::forward<Fs>(fs). If you used that instead, then only move construction would be used, instead of copies.
If the goal is to also avoid the move construction, the usual approach which constructs overloads in the caller already achieves that:
template<typename... Ts, typename Fs>
constexpr inline auto transform(const std::variant<Ts...> &var, Fs && fs)
-> decltype(auto) { return std::visit(std::forward<Fs>(fs), var); }
int main()
{
transform(
std::variant<int, double>{1.0},
overloads{
[](int) { return 1; },
[](double) { return 2; }});
return 0;
}
This uses aggregate-initialization of overloads from prvalues, which means mandatory copy elision applies and no lambdas will be copied or moved.
The std::ref approach, even if it did work, would also waste memory to store the references for non-capturing lambdas.
I don't understand where the requirement for moving comes from. I can't find it in forward_range and sized_sentinel...
Basic example:
#include <ranges>
#include <string>
#include <iostream>
class vrange: public std::ranges::view_interface<vrange>
{
public:
vrange(std::string &d): data(d){;};
vrange(const vrange &&) = delete;
auto begin() const noexcept { return data.begin(); };
auto end() const noexcept { return data.end(); };
private:
std::string data;
};
int main(){
std::string h("Hello world");
vrange r(h);
std::cout << r.size() << std::endl;
for (const auto &i: r){
std::cout << i;
}
std::cout << std::endl;
}
removing the call to r.size(), or defaulting the vrange move constructor and assignment operator makes it compile fine.
compiler message:
/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/bits/ranges_util.h: In instantiation of ‘constexpr _Derived& std::ranges::view_interface<_Derived>::_M_derived() [with _Derived = vrange]’:
/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/bits/ranges_util.h:101:35: required from ‘constexpr bool std::ranges::view_interface<_Derived>::empty() requires forward_range<_Derived> [with _Derived = vrange]’
w.cpp:25:12: required from here
/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/bits/ranges_util.h:70:23: error: static assertion failed
70 | static_assert(view<_Derived>);
| ^~~~~~~~~~~~~~
/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/bits/ranges_util.h:70:23: note: constraints not satisfied
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/ranges:37:
/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/concepts:136:13: required for the satisfaction of ‘constructible_from<_Tp, _Tp>’ [with _Tp = vrange]
/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/concepts:150:13: required for the satisfaction of ‘move_constructible<_Tp>’ [with _Tp = vrange]
/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/concepts:247:13: required for the satisfaction of ‘movable<_Tp>’ [with _Tp = vrange]
/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/concepts:137:30: note: the expression ‘is_constructible_v<_Tp, _Args ...> [with _Tp = vrange; _Args = {vrange}]’ evaluated to ‘false’
137 | = destructible<_Tp> && is_constructible_v<_Tp, _Args...>;
This has nothing to do with size specifically.
view_interface is used to build a type that is a view. Well, the ranges::view concept requires that the type is at least moveable. And view_interface has a very specific requirement on the type given as its template argument:
Before any member of the resulting specialization of view_interface other than special member functions is referenced, D shall be complete, and model both derived_from<view_interface<D>> and view.
Well, your type does not model view because it is not moveable. So you broke the rules, and you therefore get undefined behavior. Which can include compile errors happening if you call certain members but not others.
I have a class MyVariable that holds an object and does some extra work when this object has to be modified. Now I want to specialize this to MyContainer for container objects that perform this extra work only when the container itself is modified (e.g. via push_back()) but not its elements.
My code looks like this:
template<typename T>
class MyVariable
{
public:
//read-only access if fine
const T* operator->() const {return(&this->_element);}
const T& operator*() const {return( this->_element);}
//write acces via this function
T& nonconst()
{
//...here is some more work intended...
return(this->_element);
}
protected:
T _element;
};
template<typename T>
class MyContainer: public MyVariable<T>
{
public:
template<typename Arg>
auto nonconst_at(Arg&& arg) -> decltype(MyVariable<T>::_element.at(arg))
{
//here I want to avoid the work from MyVariable<T>::nonconst()
return(this->_element.at(arg));
}
};
#include <vector>
int main()
{
MyContainer<std::vector<float>> container;
container.nonconst()={1,3,5,7};
container.nonconst_at(1)=65;
}
However, with GCC4.7.2 I get an error that I cannot access _element because it is protected.
test1.cpp: In substitution of 'template<class Arg> decltype (MyVariable<T>::_element.at(arg)) MyContainer::nonconst_at(Arg&&) [with Arg = Arg; T = std::vector<float>] [with Arg = int]':
test1.cpp:39:25: required from here
test1.cpp:17:4: error: 'std::vector<float> MyVariable<std::vector<float> >::_element' is protected
test1.cpp:26:7: error: within this context
test1.cpp: In member function 'decltype (MyVariable<T>::_element.at(arg)) MyContainer<T>::nonconst_at(Arg&&) [with Arg = int; T = std::vector<float>; decltype (MyVariable<T>::_element.at(arg)) = float&]':
test1.cpp:17:4: error: 'std::vector<float> MyVariable<std::vector<float> >::_element' is protected
test1.cpp:39:25: error: within this context
test1.cpp: In instantiation of 'decltype (MyVariable<T>::_element.at(arg)) MyContainer<T>::nonconst_at(Arg&&) [with Arg = int; T = std::vector<float>; decltype (MyVariable<T>::_element.at(arg)) = float&]':
test1.cpp:39:25: required from here
test1.cpp:17:4: error: 'std::vector<float> MyVariable<std::vector<float> >::_element' is protected
test1.cpp:26:7: error: within this context
What's going on here?
The problem does seem to be specific to the use of decltype() - if I explicitly declare nonconst_at() to return T::value_type&, thus:
template<typename Arg>
typename T::value_type& nonconst_at(Arg&& arg)
then GCC 4.8.2 compiles it with no warnings or errors. That's fine for standard containers, but obviously doesn't help every situation.
Actually calling this->_element.at(arg) isn't a problem: I can omit the trailing return type and have the compiler infer it:
template<typename Arg>
auto& nonconst_at(Arg&& arg)
{
//here I want to avoid the work from MyVariable<T>::nonconst()
return this->_element.at(std::forward<Arg>(arg));
}
with just a warning (which disappears with -std=c++1y) and no errors. I still need the this->, because _element is a member of a dependent base class (thanks, Simple).
EDIT - additional workaround:
As you're only interested in the type of the return value of T::at(), you can use the decltype of calling it with any T you like, even a null pointer:
template<typename Arg>
auto nonconst_at(Arg&& arg) -> decltype(((T*)nullptr)->at(arg))
{
//here I want to avoid the work from MyVariable<T>::nonconst()
return this->_element.at(std::forward<Arg>(arg));
}
It's ugly, but it does seem to work.
Let me start with explaining what I try to accomplish. I need to create a type-erased functor (using templates and virtual functions) that would be able to "emplace" a new object in the storage of message queue for the RTOS I'm developing. Such "trickery" is required, because I want most of message queue's code to be non-templated, with only the parts that really need the type info implemented as such type-erased functors. This is a project for embedded microcontrollers (*), so please assume that I just cannot make whole message queue with a template, because ROM space is not unlimited in such environment.
I already have functors that can "copy-construct" and "move-construct" the object into the queue's storage (for "push" operations) and I also have a functor that can "swap" the object out of the queue's storage (for "pop" operations). To have a complete set I need a functor that will be able to "emplace" the object into the queue's storage.
So here is the minimum example that exhibits the problem I'm facing with creating it. Do note that this is a simplified scenario, which doesn't show much of the boiler plate (there are no classes, no inheritance and so on), but the error is exactly the same, as the root cause is probably the same too. Also please note, that the use of std::bind() (or a similar mechanism that would NOT use dynamic allocation) is essential to my use case.
#include <functional>
template<typename T, typename... Args>
void emplacer(Args&&... args)
{
T value {std::forward<Args>(args)...};
}
template<typename T, typename... Args>
void emplace(Args&&... args)
{
auto boundFunction = std::bind(emplacer<T, Args...>,
std::forward<Args>(args)...);
boundFunction();
}
int main()
{
int i = 42;
emplace<int>(i); // <---- works fine
emplace<int>(42); // <---- doesn't work...
}
When compiled on PC with g++ -std=c++11 test.cpp the first instantiation (the one using a variable) compiles with no problems, but the second one (which uses a constant 42 directly) throws this error messages:
test.cpp: In instantiation of ‘void emplace(Args&& ...) [with T = int; Args = {int}]’:
test.cpp:21:17: required from here
test.cpp:13:16: error: no match for call to ‘(std::_Bind<void (*(int))(int&&)>) ()’
boundFunction();
^
In file included from test.cpp:1:0:
/usr/include/c++/4.9.2/functional:1248:11: note: candidates are:
class _Bind<_Functor(_Bound_args...)>
^
/usr/include/c++/4.9.2/functional:1319:2: note: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) [with _Args = {_Args ...}; _Result = _Result; _Functor = void (*)(int&&); _Bound_args = {int}]
operator()(_Args&&... __args)
^
/usr/include/c++/4.9.2/functional:1319:2: note: template argument deduction/substitution failed:
/usr/include/c++/4.9.2/functional:1315:37: error: cannot bind ‘int’ lvalue to ‘int&&’
= decltype( std::declval<_Functor>()(
^
/usr/include/c++/4.9.2/functional:1333:2: note: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) const [with _Args = {_Args ...}; _Result = _Result; _Functor = void (*)(int&&); _Bound_args = {int}]
operator()(_Args&&... __args) const
^
/usr/include/c++/4.9.2/functional:1333:2: note: template argument deduction/substitution failed:
/usr/include/c++/4.9.2/functional:1329:53: error: invalid initialization of reference of type ‘int&&’ from expression of type ‘const int’
typename add_const<_Functor>::type>::type>()(
^
/usr/include/c++/4.9.2/functional:1347:2: note: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) volatile [with _Args = {_Args ...}; _Result = _Result; _Functor = void (*)(int&&); _Bound_args = {int}]
operator()(_Args&&... __args) volatile
^
/usr/include/c++/4.9.2/functional:1347:2: note: template argument deduction/substitution failed:
/usr/include/c++/4.9.2/functional:1343:70: error: invalid initialization of reference of type ‘int&&’ from expression of type ‘volatile int’
typename add_volatile<_Functor>::type>::type>()(
^
/usr/include/c++/4.9.2/functional:1361:2: note: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) const volatile [with _Args = {_Args ...}; _Result = _Result; _Functor = void (*)(int&&); _Bound_args = {int}]
operator()(_Args&&... __args) const volatile
^
/usr/include/c++/4.9.2/functional:1361:2: note: template argument deduction/substitution failed:
/usr/include/c++/4.9.2/functional:1357:64: error: invalid initialization of reference of type ‘int&&’ from expression of type ‘const volatile int’
typename add_cv<_Functor>::type>::type>()(
I tried looking for inspiration in other places, but Intel's TBB library which has a similar code (concurent_queue) with similar functionality (there's an emplace function) is actually no emplace at all - it constructs the object instantly and just "moves" it into the queue...
Any idea what's wrong with the code above? I suppose it's something really small, but I just cannot solve that myself...
(*) - https://github.com/DISTORTEC/distortos
You've already had an explanation of how that is just how std::bind works (it turns everything into an lvalue), and to use a lambda instead. However, that is not exactly trivial. Lambdas can capture by value, or by reference. You sort of need a mix of both: rvalue references should be assumed to possibly reference temporaries, so should be captured by value, with move semantics. (Note: that does mean that the original object gets moved from before the lambda gets invoked.) Lvalue references should be captured by reference, for probably obvious reasons.
One way to make this work is to manually put the captured arguments in a tuple of lvalue reference types and non-reference types, and unpack when you want to invoke the function:
template <typename T>
struct remove_rvalue_reference {
typedef T type;
};
template <typename T>
struct remove_rvalue_reference<T &&> {
typedef T type;
};
template <typename T>
using remove_rvalue_reference_t = typename remove_rvalue_reference<T>::type;
template <typename F, typename...T, std::size_t...I>
decltype(auto) invoke_helper(F&&f, std::tuple<T...>&&t,
std::index_sequence<I...>) {
return std::forward<F>(f)(std::get<I>(std::move(t))...);
}
template <typename F, typename...T>
decltype(auto) invoke(F&&f, std::tuple<T...>&&t) {
return invoke_helper<F, T...>(std::forward<F>(f), std::move(t),
std::make_index_sequence<sizeof...(T)>());
}
template<typename T, typename... Args>
void emplacer(Args&&... args) {
T{std::forward<Args>(args)...};
}
template<typename T, typename...Args>
void emplace(Args&&...args)
{
auto boundFunction =
[args=std::tuple<remove_rvalue_reference_t<Args>...>{
std::forward<Args>(args)...}]() mutable {
invoke(emplacer<T, Args...>, std::move(args));
};
boundFunction();
}
When calling emplace with args T1 &, T2 &&, the args will be captured in a tuple<T1 &, T2>. The tuple gets unpacked (thanks to #Johannes Schaub - litb for the basic idea) when finally invoking the function.
The lambda needs to be mutable, to allow that captured tuple to be moved from when invoking the function.
This uses several C++14 features. Most of these can be avoided, but I don't see how to do this without the ability to specify an initialiser in the capture list: C++11 lambdas can only capture by reference (which would be reference to the local variable), or by value (which would make a copy). In C++11, I think that means the only way to do it is not use a lambda, but effectively re-create most of std::bind.
To expand on #T.C.'s comment, you can make the code work by changing the type of the created emplacer.
auto boundFunction = std::bind(emplacer<T, Args&...>,
std::forward<Args>(args)...);
Notice the & right after Args. The reason is you're passing an rvalue to the emplace function which in turn creates emplacer(int&&). std::bind however always passes an lvalue (because it comes from its internals). With the change in place, the signature changes to emplacer(int&) (after reference collapsing) which can bind to an lvalue.
The definition for make_pair in the MSVC++ "utility" header is:
template<class _Ty1,
class _Ty2> inline
pair<_Ty1, _Ty2> make_pair(_Ty1 _Val1, _Ty2 _Val2)
{ // return pair composed from arguments
return (pair<_Ty1, _Ty2>(_Val1, _Val2));
}
I use make_pair all the time though without putting the argument types in angle brackets:
map<string,int> theMap ;
theMap.insert( make_pair( "string", 5 ) ) ;
Shouldn't I have to tell make_pair that the first argument is std::string and not char* ?
How does it know?
Function template calls can usually avoid explicit template arguments (ie make_pair<…>) by argument deduction, which is defined by C++03 §14.8.2. Excerpt:
When a function template
specialization is referenced, all of
the template arguments must have
values. The values can be either
explicitly specified or, in some
cases, deduced from the use.
The specific rules are a bit complicated, but typically it "just works" as long as you have only one specialization which is generally qualified enough.
Your example uses two steps of deduction and one implicit conversion.
make_pair returns a pair<char const*, int>,
then template<class U, classV> pair<string,int>::pair( pair<U,V> const & ) kicks in with U = char*, V = int and performs member-wise initialization,
invoking string::string(char*).
It doesn't. make_pair generated a pair<char*,int> (or maybe a pair<char const*,int>).
However, if you'll note in the implementation of pair there's a templated copy constructor:
template < typename Other1, typename Other2 >
pair(pair<Other1,Other2>& other)
: first(other.first), second(other.second)
{}
This may be implemented in slightly different ways but amounts to the same thing. Since this constructor is implicit, the compiler attempts to create pair<std::string,int> out of your pair<char*,int> - since the necessary types are convertible this works.
make_pair() exists precisely so that argument type deduction can be used to determine the template parameter types.
See this SO question: Using free function as pseudo-constructors to exploit template parameter deduction
It relies on the fact that the constructor of std::string accepts a const char*.
It doesn't matter if this constructor of std::string is explicit or not. The template deducts the type and uses the copy constructor of pair to convert it. It also doesn't matter whether or not the pair constructor is explicit.
If you turn the constructor of std::string into:
class string
{
public:
string(char* s)
{
}
};
you get this error:
/usr/include/c++/4.3/bits/stl_pair.h: In constructor ‘std::pair<_T1, _T2>::pair(const std::pair<_U1, _U2>&) [with _U1 = const char*, _U2 = int, _T1 = const string, _T2 = int]’:
foo.cpp:27: instantiated from here
/usr/include/c++/4.3/bits/stl_pair.h:106: error: invalid conversion from ‘const char* const’ to ‘char*’
/usr/include/c++/4.3/bits/stl_pair.h:106: error: initializing argument 1 of ‘string::string(char*)’
The constructor looks like this:
template<class _U1, class _U2>
pair(const pair<_U1, _U2>& __p)
: first(__p.first),
second(__p.second) { }
The copy constructor looks like this:
template<class _U1, class _U2>
pair(const pair<_U1, _U2>& __p)
: first(__p.first),
second(__p.second) { }