Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
256 views
in Technique[技术] by (71.8m points)

c++ - How to have template type deduced in std::function arguments with lambda?

I have a boost::variant and I would like to execute a functor only if the variant is of a special type, so I made up this function:

template<typename T, typename Variant>
void if_init(Variant& opt_variant, std::function<void(T)> functor){
    if(auto* ptr = boost::get<T>(&opt_variant)){
        functor(*ptr);
    }   
}

This works well, but I would like the type T to be deduced, so that I can write that:

if_init(b, [](double var){ std::cout << "I'm double and set" << std::endl; });

But the type is not deduced:

type_inference.cpp:19:5: error: no matching function for call to 'if_init'
    if_init(b, [](double var){ std::cout << "I'm double and set" << std::endl; }); 
    ^~~~~~~
type_inference.cpp:10:6: note: candidate template ignored: failed template argument deduction
    void if_init(Variant& opt_variant, std::function<void(T)> functor){

If I write:

if_init<double>(b, [](double var){ std::cout << "I'm double and set" << std::endl; });

it works well.

Is there a way to have type T being deduced ? I would like to type T only once. Here the type is short, but in the real case, there are long types.

I'm using CLang 3.2.

Here is the full test case (the first call compiles not the second):

#include <iostream>
#include <functional>
#include <boost/variant.hpp>

typedef boost::variant<int, double> Test;

template<typename T, typename Variant>
void if_init(Variant& opt_variant, std::function<void(T)> functor){
    if(auto* ptr = boost::get<T>(&opt_variant)){
        functor(*ptr);
    }   
}

int main(){
    Test b = 1.44; 

    if_init<double>(b, [](double var){ std::cout << "I'm double and set" << std::endl; });
    if_init(b, [](int var){ std::cout << "I'm int and set" << std::endl; });      

    return 0;
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

I recommend you think of std::function<Sig> as a container of any one functor that conforms to Sig as a signature -- and which can be replaced at any moment. This functionality comes in very handy for e.g. std::vector<std::function<Sig>> because such a container can then hold functors of different types.

In your case, because you only care to have just the one functor you really don't need the functionality of std::function<Sig>. As such, I recommend you declare your function template like so:

template<typename T, typename Variant, typename Functor>
void if_init(Variant& opt_variant, Functor functor);

If you are worried that this doesn't communicate that Functor must conform to a void(T) signature, please note that std::function<Sig> does not enforce that either: although obviously you will end up with a compilation error, it is not a nice one. It's planned to be changed (and maybe your implementation has that either), but changed to a different kind of error. Still not that helpful for your case.

I personally make use of template aliases (in the template parameter list) to both document and enforce what a functor should conform to. This ends up looking like:

// Documents that e.g. long l = std::forward<Functor>(functor)(42.)
// should be a valid expression -- a functor that returns int would
// also be accepted.
// Triggers a hard-error (typically a static_assert with a nice message)
// on violation.
template<typename Functor, Requires<is_callable<Functor, long(double)>>...>
R foo(Functor functor);

// Documents that this function template only participates in overload resolution
// if the functor conforms to the signature.
// Does not trigger a hard-error (necessary by design); if everything goes right
// then another overload should be picked up -- otherwise an error of the kind
// 'no matching overload found' is produced
template<typename Functor, EnableIf<is_callable<Functor, long(double)>>...>
R bar(Functor functor);

As to your exact question, the rules of C++ do not allow for a template parameter to be deduced in your case. It's really not an easily fixed 'problem', if it is one. You can find more information on this.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...