I am in the process of fixing some of the sonarcloud issues on my project. One complaint is the function where I read and deserialize the settings for my application. Basically there are 50 or so blocks of this in one function:
if (!m_logLevel.deserializeFromSettings(settings, HGGW_KEY_LOGLEVEL) || m_logLevel.value() <= 0 || m_logLevel.value() > 4)
{
m_logLevel.update(HGGW_DEFAULT_LOGLEVEL, 0);
m_logLevel.serializeToQSettings(settings, HGGW_KEY_LOGLEVEL);
}
The members are instances of a templated class:
template <typename T>
class AwsProperty
{
public:
AwsProperty(T value, qint64 timestamp) : m_timestamp(timestamp), m_data(value){}
//other stuff, not important
}
//example:
AwsProperty<int> m_property;
I tried to design this function:
template <typename X>
void readAWSSettingOrPopulateDefault(AwsProperty<X> property, QString key, X default_val, QSettings settings, bool (*predicate)(X)){
if(!property.deserializeFromSettings(settings,key) || predicate(property.value())){
property.update(default_val, 0);
property.serializeToQSettings(settings, key);
}
}
Trying to call it:
this->readAWSSettingOrPopulateDefault(m_logLevel, HGGW_KEY_LOGLEVEL, HGGW_DEFAULT_LOGLEVEL, settings, [](int val){return val <= 0 || val > 4;});
Yields this error:
XXXXconfig.cpp:275: error: no matching function for call to ‘LocalConfig::readAWSSettingOrPopulateDefault(HGGWAwsPropertyInt32&, const QString&, const int&, QSettings&, LocalConfig::init(QString)::<lambda(int)>)’
this->readAWSSettingOrPopulateDefault(m_logLevel, HGGW_KEY_LOGLEVEL, HGGW_DEFAULT_LOGLEVEL, settings, [](int val){return val <= 0 || val > 4;});
XXXXconfig.cpp:275: note: mismatched types ‘bool (*)(X)’ and ‘LocalConfig::init(QString)::<lambda(int)>’
this->readAWSSettingOrPopulateDefault(m_logLevel, HGGW_KEY_LOGLEVEL, HGGW_DEFAULT_LOGLEVEL, settings, [](int val){return val <= 0 || val > 4;});
^ ^
XXXXconfig.cpp:275: note: mismatched types ‘bool (*)(X)’ and ‘LocalConfig::init(QString)::<lambda(int)>’
^
The reason your code reports an error is that for a function template declared as:
template <typename X>
void readAWSSettingOrPopulateDefault(AwsProperty<X> property, QString key
, X default_val, QSettings settings
, bool (*predicate)(X));
the compiler will have to deduce X from all arguments using this template parameter -- property, default_val and predicate -- and then make sure that those deduced types are the same (that is, there's only one template parameter, it can hold one type).
However, a lambda is not a function. It is convertible to a function pointer through an implicit conversion. Template argument deduction does not consider conversions (except for the derived-to-base conversion). Hence, the compiler is unable to finalize template argument deduction.
In order to make your code work, put the last occurrence of X in the function pointer-type parameter declaration in a non-deduced context:
template <typename T>
struct dont_deduce { using type = T; };
template <typename T>
using dont_deduce_t = typename dont_deduce<T>::type;
template <typename X>
void readAWSSettingOrPopulateDefault(AwsProperty<X> property, QString key
, X default_val, QSettings settings
, bool (*predicate)(dont_deduce_t<X>));
This way, the compiler will not even attempt to deduce X from the lambda expression, and instead will use the type from property/default_val, thus making it possible to use the implicit conversion of the lambda to a function pointer. That is, no deduction and at the same time, a known function signature that can trigger the conversion.
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.
Sorry for the generic title, but I'm unable to focus the problem.
I have a templatized class method that accept an argument pack and provides a new type in return, to hide the details of the implementation. More specifically, the class handles SQLite queries, and the method calls sqlite3_prepare() to prepare the statement before executing the query.
class Table {
...
template <typename ...Ts>
class PreparedStatement { ... };
template <typename ...Ts>
PreparedStatement<Ts...> prepare(std::tuple<Ts...> tuple) {
// do something
return PreparedStatement<Ts...> ( ... );
}
That works well with "normal" types, but the problem occurs when the arguments are declared const:
const Field<int> fld = createField<int>("name");
...
PreparedStatement<decltype(fld)> s = prepare(make_tuple(fld));
The error is the following:
no match for 'operator =' (operand types are PreparedStatenent<const Field<int>> and PreparedStatement<Field<int>>
I suspect the issue is in my declaration of the function, is there a way to fix this issue and make the function more "elegant" ?
NOTE: I know I can fix the issue by manually declare the s variable, but my doubts are on how the method was implemented.
As Many Asked, here's an example:
#include <tuple>
template <typename T>
struct Field {
};
class Table {
public:
template <typename ...Ts>
class PreparedStatement {
public:
PreparedStatement() {};
};
template <typename ...Ts>
PreparedStatement<Ts...> prepare(std::tuple<Ts...> tuple) {
// do something
return PreparedStatement<Ts...> ( );
}
};
int main()
{
Field<int> fld;
Table t;
Table::PreparedStatement<decltype(fld)> p;
p = t.prepare(std::make_tuple(fld));
// here comes the problem
const Field<int> f2;
Table::PreparedStatement<decltype(f2)> p2;
p2 = t.prepare(std::make_tuple(f2));
return 0;
}
and here's the compiler output
main.cpp: In function 'int main()': main.cpp:35:39: error: no match
for 'operator=' (operand types are 'Table::PreparedStatement >' and 'Table::PreparedStatement >')
p2 = t.prepare(std::make_tuple(f2));
^ main.cpp:10:10: note: candidate: constexpr Table::PreparedStatement >&
Table::PreparedStatement >::operator=(const
Table::PreparedStatement >&)
class PreparedStatement {
^~~~~~~~~~~~~~~~~ main.cpp:10:10: note: no known conversion for argument 1 from 'Table::PreparedStatement >'
to 'const Table::PreparedStatement >&'
main.cpp:10:10: note: candidate: constexpr
Table::PreparedStatement >&
Table::PreparedStatement
::operator=(Table::PreparedStatement >&&) main.cpp:10:10: note: no known conversion for argument 1 from
'Table::PreparedStatement >' to
'Table::PreparedStatement >&&'
UPDATE
As many noted, I could use auto to deduce the type, but in some condition auto cannot practically be used. One is, for example, if I need to declare the statement in the Class Context.
So suppose auto is forbidden for some reason. Isn't any other solution available? See the updated code above.
cppreference.com for make_tuple tells us:
template< class... Types >
tuple<VTypes...> make_tuple( Types&&... args );
For each Ti in Types..., the corresponding type Vi in Vtypes... is
std::decay<Ti>::type unless application of std::decay results in
std::reference_wrapper<X> for some type X, in which case the deduced
type is X&.
While std::decay, among other things, removes cv-qualifiers. So your type will be no PreparedStatement<const Field<int>>, but PreparedStatement<Field<int>>.
You can use auto, as manni66 proposed, to avoid such problems.
auto s = prepare(make_tuple(fld));
I could use auto to deduce the type, but in some condition auto cannot practically be used. One is, for example, if I need to declare the statement in the Class Context. So suppose auto is forbidden for some reason. Isn't any other solution available? See the updated code above.
Instead of auto, you can use a decltype expression that take in count the value returned by prepare.
I mean... instead of
Table::PreparedStatement<decltype(f2)> p2;
you can try with
decltype(t.prepare(std::make_tuple(f2))) p2;
or
decltype(std::declval<Table>().prepare(
std::make_tuple(std::declval<Field<int>>()))) p2;
I suppose you can use a similar decltype() also to declare members of your classes.
Why doesn't the following code compile (in C++11 mode)?
#include <vector>
template<typename From, typename To>
void qux(const std::vector<From>&, To (&)(const From&)) { }
struct T { };
void foo(const std::vector<T>& ts) {
qux(ts, [](const T&) { return 42; });
}
The error message is:
prog.cc:9:5: error: no matching function for call to 'qux'
qux(ts, [](const T&) { return 42; });
^~~
prog.cc:4:6: note: candidate template ignored: could not match 'To (const From &)' against '(lambda at prog.cc:9:13)'
void qux(const std::vector<From>&, To (&)(const From&)) { }
^
But it doesn't explain why it couldn't match the parameter.
If I make qux a non-template function, replacing From with T and To with int, it compiles.
A lambda function isn't a normal function. Each lambda has its own type that is not To (&)(const From&) in any case.
A non capturing lambda can decay to To (*)(const From&) in your case using:
qux(ts, +[](const T&) { return 42; });
As noted in the comments, the best you can do to get it out from a lambda is this:
#include <vector>
template<typename From, typename To>
void qux(const std::vector<From>&, To (&)(const From&)) { }
struct T { };
void foo(const std::vector<T>& ts) {
qux(ts, *+[](const T&) { return 42; });
}
int main() {}
Note: I assumed that deducing return type and types of the arguments is mandatory for the real problem. Otherwise you can easily deduce the whole lambda as a generic callable object and use it directly, no need to decay anything.
If you don't need to use the deduced To type, you can just deduce the type of the whole parameter:
template<typename From, typename F>
void qux(const std::vector<From>&, const F&) { }
Correct me if I am wrong, but template parameters deduction deduces only exact types without considering possible conversions.
As a result the compiler cannot deduce To and From for To (&)(const From&) because qux expects a reference to function, but you provide a lambda which has its own type.
You have left absolutely no chance to compiler to guess what is To. Thus, you need to specify it explicitly.
Also, lambda here needs to be passed by pointer.
Finally, this version compiles ok:
template<typename From, typename To>
void qux(const std::vector<From>&, To (*)(const From&)) { }
struct T { };
void foo(const std::vector<T>& ts) {
qux<T,int>(ts,[](const T&) { return 42; });
}
You're expecting both implicit type conversions (from unnamed function object type to function reference type) and template type deduction to happen. However, you can't have both, as you need to know the target type to find the suitable conversion sequence.
But it doesn't explain why it couldn't match the parameter.
Template deduction tries to match the types exactly. If the types cannot be deduced, deduction fails. Conversions are never considered.
In this expression:
qux(ts, [](const T&) { return 42; });
The type of the lambda expression is some unique, unnamed type. Whatever that type is, it is definitely not To(const From&) - so deduction fails.
If I make qux a non-template function, replacing From with T and To with int, it compiles.
That is not true. However, if the argument was a pointer to function rather than a reference to function, then it would be. This is because a lambda with no capture is implicitly convertible to the equivalent function pointer type. This conversion is allowed outside of the context of deduction.
template <class From, class To>
void func_tmpl(From(*)(To) ) { }
void func_normal(int(*)(int ) ) { }
func_tmpl([](int i){return i; }); // error
func_tmpl(+[](int i){return i; }); // ok, we force the conversion ourselves,
// the type of this expression can be deduced
func_normal([](int i){return i; }); // ok, implicit conversion
This is the same reason why this fails:
template <class T> void foo(std::function<T()> );
foo([]{ return 42; }); // error, this lambda is NOT a function<T()>
But this succeeds:
void bar(std::function<int()> );
bar([]{ return 42; }); // ok, this lambda is convertible to function<int()>
The preferred approach would be to deduce the type of the callable and pick out the result using std::result_of:
template <class From,
class F&&,
class To = std::result_of_t<F&&(From const&)>>
void qux(std::vector<From> const&, F&& );
Now you can pass your lambda, or function, or function object just fine.
include
template <typename R, typename S, typename T>
T const min (R const& a, S const& b)
{
T val;
if( a > b )
{
val = static_cast<T>( b );
}
else
{
val = static_cast<T>( a );
}
return val;
}
// CANNOT change anything above this line --------------------
void B()
{
int val =0;
// Only change the next line
val = min (5,4.2);
assert( val == 4 );
}
when the code is compiled the following error is thrown
error C2783: 'const T min(const R &,const S &)' : could not deduce template argument for 'T'
Stuck trying to solve this.. . any help will be appreciated
The compiler error is telling you that it can't figure out what type T is supposed to be in your call to min, since you haven't specified it and the return type of a function or function template are not used during overload resolution or template argument deduction (unless it's a conversion operator, of course).
Since you can't change the definition of min (which is stupid) your only choice is to explicitly specify T in the call. However, since T is the last template parameter, you have to specify the two preceding template arguments too! Like this:
val = min<int, double, int>(5, 4.2);
The compiler cannot deduce the template argument if you don't use it.
That being said what you would do in this situation is specify in specify the template arguments when you call the function min. Just like this:
void B()
{
int val = 0;
// Only change the next line
val = min<int, double, int>(5, 4.2);
assert(val == 4);
}
Trying to using a template, where the argument is
template<class T, boost::function<long (T*)> &f>
static long myFunc(const vector<boost::shared_ptr<T>> &inputVector)
{ // do stuff}
This is the call I make
long i = myFunc<MyClass, boost::bind(&MyClass::myClassFunc, _1)>(myInputVector);
where the signature of the function is
long myClassFunc() const { return m_value; }
Getting the following compiler error:
error C2975: 'f' : invalid template argument for 'myFunc', expected compile-time constant expression
What do I need to get this to compile?
Binding arguments to a function is a run-time operation. When you pass a value as a template parameter, the value has to be known at compile time. Pass the boost::function as an argument.
template<class T>
static long myFunc(const vector<boost::shared_ptr<T>> &inputVector, boost::function<long (T*)> &f)
{ // do stuff
}
call it like that:
long i = myFunc<MyClass)>(myInputVector, boost::bind(&MyClass::myClassFunc, _1));