I have the following method declaration on a basic_buffer class:
const_iterator insert(const_iterator position, typename argument<value_type>::type val)
Notice the type of the second argument. I often use this argument traits that basically decides whether the argument should be passed by copy or by reference when receiving template arguments. In this case, value_type is a typedef of the template argument T. For instance, fundamental types should be passed by copy instead of const reference. Here's the implementation:
template <typename T> struct argument
{
typedef std::conditional<std::is_fundamental<T>::value || std::is_pointer<T>::value, const T, const T &> type;
};
Notice how fundamental and pointer types evaluate to const T and other types evaluate to const T &. This has been working great so far.
Now consider the following function:
template <class T>
void foo()
{
typedef basic_buffer<T> _storage_type;
typedef typename _storage_type::value_type _value_type;
_value_type value = 0;
_storage_type _storage;
_storage.insert(_storage.end(), value);
}
Several details are omitted. This is what I get:
error: no matching member function for call to 'insert'
_storage.insert(_storage.end(), value);
~~~~~~~~~^~~~~~
What surprises me is this overload version not being matched:
note: candidate function not viable: no known conversion from '_value_type' (aka 'unsigned char') to 'typename argument<value_type>::type' (aka 'conditional<std::is_fundamental<unsigned
char>::value || std::is_pointer<unsigned char>::value, const unsigned char, const unsigned char &>') for 2nd argument
const_iterator insert(const_iterator position, typename argument<value_type>::type val)
To make matters even more confusing, if I cast value to _value_type (that, notably, is already its type) it works:
_storage.insert(_storage.end(), static_cast<_value_type>(value));
So I can solve this by casting value, but rather not. What is happening here?
You have
typedef std::conditional<std::is_fundamental<T>::value || std::is_pointer<T>::value, const T, const T &> type;
So type is a std::conditional<std::is_fundamental<T>::value || std::is_pointer<T>::value, const T, const T &>
When you call
_storage.insert(_storage.end(), value);
It is trying to convert value to a std::conditional<std::is_fundamental<T>::value || std::is_pointer<T>::value, const T, const T &>
You need to add ::type to the conditional to get the resulting type from the condition.
typedef std::conditional<std::is_fundamental<T>::value || std::is_pointer<T>::value, const T, const T &>::type type;
Related
I have the following templated method:
auto clusters = std::vector<std::pair<std::vector<long>, math::Vector3f>>
template<class T>
void eraserFunction(std::vector<T>& array, std::function<int(const T&, const T&)> func)
{
}
And I have a function that looks like
auto comp1 = [&](
const std::pair<std::vector<long>, math::Vector3f>& n1,
const std::pair<std::vector<long>, math::Vector3f>& n2
) -> int {
return 0;
};
math::eraserFunction(clusters, comp1);
However, I get a syntax error saying:
116 | void eraserFunction(std::vector<T>& array, std::function<int(const T&, const T&)> func)
| ^~~~~~~~~~~~~~
core.hpp:116:6: note: template argument deduction/substitution failed:
geom.cpp:593:23: note: 'math::method(const at::Tensor&, const at::Tensor&, int, float, int, int, float)::<lambda(const std::pair<std::vector<long int>, Eigen::Matrix<float, 3, 1> >&, const std::pair<std::vector<long int>, Eigen::Matrix<float, 3, 1> >&)>' is not derived from 'std::function<int(const T&, const T&)>'
593 | math::eraserFunction(clusters, comp1);
The function call tries to deduce T from both the first and second function parameter.
It will correctly deduce T from the first parameter, but fail to deduce it from the second parameter, because the second function argument is a lambda type, not a std::function type.
If deduction isn't possible from all parameters that are deduced context, deduction fails.
You don't really need deduction from the second parameter/argument here, since T should be fully determined by the first argument. So you can make the second parameter a non-deduced context, for example by using std::type_identity:
void eraserFunction(std::vector<T>& array, std::type_identity_t<std::function<int(const T&, const T&)>> func)
This requires C++20, but can be implemented easily in user code as well if you are limited to C++11:
template<typename T>
struct type_identity { using type = T; };
and then
void eraserFunction(std::vector<T>& array, typename type_identity<std::function<int(const T&, const T&)>>::type func)
std::identity_type_t<T> is a type alias for std::identity_type<T>::type. Everything left to the scope resolution operator :: is a non-deduced context, which is why that works.
If you don't have any particular reason to use std::function here, you can also just take any callable type as second template argument:
template<class T, class F>
void eraserFunction(std::vector<T>& array, F func)
This can be called with a lambda, function pointer, std::function, etc. as argument. If the argument is not callable with the expected types, it will cause an error on instantiation of the function body containing the call. You can use SFINAE or since C++20 a type constraint to enforce this already at overload resolution time.
Here's a minimal example:
#include <iostream>
#include <type_traits>
template <typename T>
struct Foo {
typedef typename std::decay<T>::type U;
const U s;
Foo(const T& val): s(val) {}
};
template <typename T>
Foo<T> make_foo(const T& val) {
return Foo<T>(val);
}
int main() {
make_foo("foo");
}
I expect Foo::s (or U) to be const-ed, but the compiler error I'm getting (on Ubuntu WSL g++ 7.4.0) is
bar.cpp: In instantiation of ‘Foo<T>::Foo(const T&) [with T = char [4]]’:
bar.cpp:14:12: required from ‘Foo<T> make_foo(const T&) [with T = char [4]]’
bar.cpp:18:19: required from here
bar.cpp:9:28: error: invalid conversion from ‘const char*’ to ‘Foo<char [4]>::U {aka char*}’ [-fpermissive]
Foo(const T& val): s(val) {}
^
which obviously means that neither U or Foo::s is a const char*. Thoughts?
EDIT:
For anyone running into this problem, the solution is exactly as StoryTeller points out in his post below. In other words, the const I had in make_foo(const T& val) in the parameter signature was stripping the const from the type parameter and making T evaluate to char [4]. And for some reason, I couldn't even grasp putting a const in the template angle brackets of each instance of Foo<...> (the return type and the expression returned):
#include <iostream>
#include <type_traits>
template <typename T>
struct Foo {
static_assert(std::is_same<T, const char[4]>::value);
typedef typename std::decay<T>::type U;
static_assert(std::is_same<U, const char*>::value);
U s;
Foo(const T& val): s(val) {}
};
template <typename T>
Foo<const T> make_foo(const T& val) {
static_assert(std::is_same<T, char[4]>::value);
return Foo<const T>(val);
}
int main() {
make_foo("foo");
}
Also, as chris pointed out as well, it's nice to spam static_assert and std::is_same to make dealing with templates in combination with const a bit more sane.
make_foo accepts by a const T &, and you pass in a constant array of 4 characters (that's what string literals are). Template argument deduction must therefore match a const char (&)[4] against the const T &. And since the const is specified in the parameter type, that leaves T as char[4]. The const qualifier is "consumed" by the function parameter.
So when you instantiated Foo<T>, you did so with char[4]. That type will decay to a char*, not a const char*. Adding const on top of it will only produce a char * const.
If you wish to preserve the const-ness of T, then amend your return statement (and type) to be
return Foo<const T>(val);
I'm learing tuples in C++ and for now I'm trying to create tuple using allocator from libcxx
template <class _Alloc>
LIBCPP_INLINE_VISIBILITY
tuple(allocator_arg_t, const _Alloc& __a, const _Tp& ... __t)
for instance:
std::allocator<int> myAllocator;
std::tuple<int> t(std::allocator_arg, myAllocator, 2);
but seems string above called
template <class Alloc>
tuple(allocator_arg_t, const Alloc& a, const Types&...);
what should I change for this?
As well, there is one row that isn't clear for me:
explicit
tuple(_Up&&... __u)
how does this call?
When you look into your implementation's source and see
namespace std {
// Other things
template <typename ... _Tp>
class tuple {
// More things
template <class _Alloc>
LIBCPP_INLINE_VISIBILITY
tuple(allocator_arg_t, const _Alloc& __a, const _Tp& ... __t)
// an implementation of this constructor
};
}
That is the constructor that cppreference names
template <class Alloc>
tuple(allocator_arg_t, const Alloc& a, const Types&...);
Your implementation has chosen to use names that are reserved for its use. What exactly those names are doesn't matter to the compiler.
what const _Tp& ... __t is?
It's a parameter pack of elements to copy into the tuple. For std::tuple<int>, it is const int&, for std::tuple<std::string, bool, char> it is const std::string &, const bool &, const char &. __t is the name of the parameter pack. C++ allows templates to have different numbers of parameters.
what about tuple(_Up&&... __u)?
That's overload (3)
Converting constructor. Initializes each element of the tuple with the corresponding value in std::forward<UTypes>(args).
This overload only participates in overload resolution if sizeof...(Types) == sizeof...(UTypes) and sizeof...(Types) >= 1 and std::is_constructible<Ti, Ui&&>::value is true for all i.
The constructor is explicit if and only if std::is_convertible<Ui&&, Ti>::value is false for at least one i.
E.g. for std::tuple<int> tup('a');, tup would be initialised by matching UTypes... with char, and the first member would have the numeric value of 'a' (97 on most platforms).
Note that there isn't much point in using an allocator-aware constructor for std::tuple<int>, because int is not a allocator-aware type. Those constructors exist for cases like
using statefully_allocated = std::vector<int, my_stateful_allocator<int>>;
my_stateful_allocator<int> alloc1 = /* something */
statefully_allocated source(alloc);
my_stateful_allocator<int> alloc2 = /* something else */
std::tuple<statefully_allocated, char> tup(std::allocator_arg, alloc2, source, 'a');
Where the statefully_allocated member copies the contents of source, but uses a copy of alloc2 to allocate. the char member is just an ordinary char, alloc2 plays no part in it's construction. See Uses-allocator construction
I have encounter serious template type deduction problem when I use method pointer in argument of a template function.
Let's take the following code:
template <class ClassT, typename Arg1T>
inline void testTemplateFct(ClassT * clazz,
void (ClassT::*fctPtr)(Arg1T),
const Arg1T & arg1)
{
}
class TestClass
{
public:
void testMethodIntArg(int arg)
{}
void testMethodDoubleArg(double arg)
{}
void testMethodStringArg(const char * arg);
};
int main()
{
TestClass testClass;
testTemplateFct(&testClass,
&TestClass::testMethodIntArg,
10);
testTemplateFct(&testClass,
&TestClass::testMethodDoubleArg,
10.0);
/// BEGINNING OF MY PROBLEM
testTemplateFct(&testClass,
&TestClass::testMethodStringArg,
"a string...");
/// END OF MY PROBLEM
return 0;
}
If I compile it using g++, I get the following error message:
$ g++ ArgumentDeduction.cpp -o ArgumentDeduction
ArgumentDeduction.cpp: In function ‘int main()’:
ArgumentDeduction.cpp:42:18: error: no matching function for call to ‘testTemplateFct(TestClass*, void (TestClass::*)(const char*), const char [12])’
"a string...");
^
ArgumentDeduction.cpp:4:13: note: candidate: template<class ClassT, class Arg1T> void testTemplateFct(ClassT*, void (ClassT::*)(Arg1T), const Arg1T&)
inline void testTemplateFct(ClassT * clazz,
^~~~~~~~~~~~~~~
ArgumentDeduction.cpp:4:13: note: template argument deduction/substitution failed:
ArgumentDeduction.cpp:42:18: note: deduced conflicting types for parameter ‘const Arg1T’ (‘const char*’ and ‘char [12]’)
"a string...");
If I remove the reference of the third argument of method testTemplateFct the problem disappears (HOWEVER I ABSOLUTELY NEED THE REFERENCE IN ORDER TO AVOID COPY)
template <class ClassT, typename Arg1T>
inline void testTemplateFct(ClassT * clazz,
void (ClassT::*fctPtr)(Arg1T),
const Arg1T arg1)
{}
I understand more or less the error message but I do not understand why there is an ambiguity between const char* and char [12]. I do not understand why the problem disappears when I remove the reference.
Finally, I would strongly appreciate any help in order to correct this code while keeping the reference
PS: I know that I can "force" the type deduction by doing:
testTemplateFct(&testClass,
&TestClass::testMethodStringArg,
(const char *) "a string...");
but I don't like it very much
Your template requires that both the occurrences of Arg1T are deduced to the same type. I believe that is not what you want. Instead the types should be deduced independently:
template <class ClassT, typename Arg1T, typename GivenT>
inline void testTemplateFct(ClassT * clazz,
void (ClassT::*fctPtr)(Arg1T),
GivenT &&arg1)
{
//example use
(clazz->*fctPtr)(std::forward<GivenT>(arg1));
}
I do not understand why there is an ambiguity between const char* and char [12].
Note that "a string..." is an array with type const char[12]. For the function template testTemplateFct, the parameter arg1 is declared as a reference, i.e. const Arg1T &, then array-to-pointer decay won't occur in template argument deduction and Arg1T is deduced as char[12], which doesn't match the deduced type of Arg1T from the 2nd argument, i.e. const char*, so deduction failed.
I do not understand why the problem disappears when I remove the reference.
When the parameter is declared as pass-by-value array-to-pointer decay is applied; then both the deduced type of Arg1T from the 2nd and 3rd argument will be const char* and everything work fine.
You have two basic options.
The first one is to change your invocation to:
testTemplateFct(&testClass,
&TestClass::testMethodStringArg,
(const char *)"a string...");
The second option is to add an overload:
template <class ClassT, size_t n>
inline void testTemplateFct(ClassT * clazz,
void (ClassT::*fctPtr)(const char *),
const char (&arg1)[n])
{
testTemplateFct<ClassT, const char *>(clazz, fctPtr, arg1);
}
Pick which one works best for you.
A literal character string is actually a const char[n], and not a const char *. The const char array decays to a const char * in an ordinary function call; but this decay does not occur as part of template deduction; hence the problem.
I'm trying to get this to work:
template<class Type>
typename boost::enable_if< boost::mpl::or_<
boost::is_arithmetic<Type>,
is_string<Type> > >::type
get(const std::string &argPath, const Type &argDefault) {
bool caught = false;
std::stringstream ss;
Type value;
try {
value = ptree_.get<Type>(argPath);
} catch(ptree_bad_path &e) {
caught = true;
}
if(caught)
value = argDefault;
ss << value;
parameters_.insert(std::pair<std::string, std::string>(argPath, ss.str()));
return value;
}
I used the following is_string type trait: Type trait for strings
My goal is to restrict my Type to string or arithmetic type so that I can push it to my stringstream.
So this builds, but when I try to use it, it returns the following errors:
error: void value not ignored as it ought to be
In member function ‘typename
boost::enable_if,
is_string, mpl_::bool_, mpl_::bool_,
mpl_::bool_ >, void>::type FooClass::get(const std::string&,
const Type&) [with Type = uint8_t]’
error: return-statement with a value, in function returning 'void'
Here is how I try to use it:
FooClass f;
item_value = f.get("tag1.tag2.item", DEFAULT_ITEM_VALUE);
Any help is appreciated, thanks in advance!
From http://www.boost.org/doc/libs/1_53_0/libs/utility/enable_if.html, enable_if has a second parameter that defaults to void:
template <bool B, class T = void>
struct enable_if_c {
typedef T type;
};
Seems to me you need to include the return type in your enable_if. (It is defaulting to void now.)
template<class Type>
typename boost::enable_if< boost::mpl::or_<
boost::is_arithmetic<Type>,
is_string<Type> >,
Type >::type
get(const std::string &argPath, const Type &argDefault);