将函数传递给可变参数模板

时间:2019-02-04 01:44:46

标签: c++ templates c++17 variadic-templates

请考虑以下功能模板:

$('.list-guides .select').on('click', function() {
$(this).parent().toggleClass('active');

if($(this).parent().hasClass('active')){

  var $title = $('.active').find('.title').text();
  alert($title);
  var $preco = 9.90;
  alert($preco);
}
  return false;
});

以及以下功能:

<div class="details">
          <div class="content-details">
          <p class="title">Detalhes da Compra</p>
          <div class="content">
            <div class="product">
              <p class="name"><span class="font-weight-bolder">1x</span> Competências Gerais na BNCC - R$17</p>
              <hr/>
              <div class="container">
                <div class="row">
                  <!--frete-->
                  <div class="col-xl-6 col-lg-6 col-md-6 col-6">
                    <p class="field">Frete:</p>
                  </div>
                  <div class="col-xl-6 col-lg-6 col-md-6 col-6">
                    <p class="shipp float-right">Grátis</p>
                  </div>
                </div>
                <!--fim frete / início desconto-->
                <div class="row">
                  <div class="col-xl-6 col-lg-6 col-md-6 col-6">
                    <p class="field">Desconto:</p>
                  </div>
                  <div class="col-xl-6 col-lg-6 col-md-6 col-6">
                    <p class="discount float-right">-R$7.10</p>
                  </div>
                </div>
                <!--fim desconto / início Total-->
                <div class="row row-total">
                  <div class="col-xl-6 col-lg-6 col-md-6 col-6">
                    <p class="field font-weight-bolder">Total:</p>
                  </div>
                  <div class="col-xl-6 col-lg-6 col-md-6 col-6">
                    <p class="total float-right">R$9.90</p>
                  </div>
                </div>
                <div class="row row-next">
                  <div class="col-xl-12 col-lg-12 col-md-12 col-12">
                    <p class="field-btn text-center"><a href="#" class="button" target="_blank">Prosseguir</a></p>
                  </div>
              </div>
            </div>
            <p class="no-product text-center">Nenhum produto selecionado</p>
          </div>
        </div>
      </div>
    </div>

为什么会发生以下情况:

template<typename RetType, typename... ArgTypes>
void foo0(std::function<RetType(ArgTypes...)> f) {}

template<typename RetType, typename ArgType>
void foo1(std::function<RetType(ArgType)> f) {}

编译错误是(带有C ++ 17的gcc-8):

void bar(int n) {}

使用虚拟模板

 foo0<void, int>(bar);  // does not compile
 foo1<void, int>(bar);  // compiles fine

使error: no matching function for call to 'foo0<void, int>(void (&)(int))' foo0<void, int>(bar); ^ note: candidate: 'template<class RetType, class ... ArgTypes> void foo0(std::function<_Res(_ArgTypes ...)>)' void foo0(std::function<RetType(ArgTypes...)> f) {} ^~~~ note: template argument deduction/substitution failed: note: mismatched types 'std::function<void(_ArgTypes ...)>' and 'void (*)(int)' foo0<void, int>(bar); 在gcc-8中可以正常编译,但是在Apple LLVM版本10.0.0(clang-1000.11.45.5)中使用clang会出错。

叮当声错误是

template<typename T>
void bar(int n) {}

2 个答案:

答案 0 :(得分:1)

  

为什么会发生以下情况?

打电话时记入

 foo0<void, int>(bar); // compilation error
 foo1<void, int>(bar); // compile

foo0()foo1()期望std::function,而bar是指向可以转换为std::function但不是std::function

foo1()情况下,您同时显露了RetTypeArgType模板参数,因此编译器可以将bar转换为std::function<void(int)>,一切正常

但是foo0()的情况有所不同,因为模板参数ArgTypes...是一个可变参数,调用foo0<void, int>(bar)不会显式显示完整的ArgTypes...可变参数列表,而只显示第一种。

如果我没记错的话,问题是编译器尝试从ArgTypes...参数推导出bar的其余部分,但是bar不是std::function因此编译器无法推断出ArgTypes...的其余部分,因此会出错。

我想是

foo0<void, int>(std::function<void(int)>{bar});

或者简单地

foo0(std::function<void(int)>{bar});

或(仅C ++ 17)

foo0(std::function{bar});

应进行编译,因为以这种方式调用foo0()时,该函数会收到一个std::function,以便编译器可以完全推断出模板参数。

我不明白带有虚拟模板参数的bar()的版本

foo0<void, int>(bar<int>);

可以用g ++-8编译,我想这是g ++错误。

答案 1 :(得分:1)

template<typename RetType, typename... ArgTypes>
void foo0(std::function<RetType(ArgTypes...)> f) {}

解决方法是:

template<class X>struct tag_t{using type=X;};
template<class X>using block_deduction = typename tag_t<X>::type;

template<typename RetType, typename... ArgTypes>
void foo0(block_deduction_t<std::function<RetType(ArgTypes...)>> f) {}

,现在您的foo0<void, int>(bar)会进行编译。

一般的问题是,说foo0<void, int>,不是说“ RetTypevoid,而ArgTypes...int,而是说{{ 1}} ArgTypes...开头。

int

以上编译正常。

...

中的另一种方法是添加另一个重载。

离开这个:

std::function<void(int, double)> x;
foo0<void, int>( x )

但添加:

template<typename RetType, typename... ArgTypes>
void foo2(block_deduction_t<std::function<RetType(ArgTypes...)>> f) {}

在这里,我们将template<typename RetType, typename... ArgTypes, class F> void foo2(F&& f) { return foo2<RetType, ArgTypes...>( std::function{std::forward<F>(f)} ); } template<int unused, class F> void foo2(F&& f) { return foo2( std::function{std::forward<F>(f)} ); } 包装到构造指导的F中。

您对std::function的调用现在将调用foo2<int, void>( bar ),这是第二个重载。然后它继续从中构造一个foo2<void, int, decltype(bar)&>,只要签名完全匹配就可以。