通用lambda

时间:2016-07-19 10:29:05

标签: c++ templates lambda c++14

以下内容无法编译:

#include <iostream>
#include <type_traits>

template <class F, class G>
auto static_if (std::true_type, F && f, G && g) {
    return std::forward<F> (f) (0);
}   

template <class F, class G>
auto static_if (std::false_type, F && f, G && g) {
    return std::forward<G> (g) (0);
}


template <class T> std::true_type constexpr is_pointer (T *) { return {}; }
template <class T> std::false_type constexpr is_pointer (T) { return {}; }

int main() {
    int x = 5;
    static_if (
        is_pointer (x),
        [&] (auto) { std::cout << *x << std::endl; },
        [&] (auto) { std::cout << "no pointer" << std::endl; }
    );
    return 0;
}

编译失败,并显示错误消息:

prog.cpp: In lambda function:
prog.cpp:23:33: error: invalid type argument of unary '*' (have 'int')
      [&] (auto) { std::cout << *x << std::endl; },

见这里:http://ideone.com/frl8rn

现在问题是:为什么?

很明显,如果lambdas不是通用的(没有auto参数),编译应该会失败。

但是我们这里有两个泛型 lambdas。它们中的每一个都只应在使用时进行实例化(这意味着:称为,或者至少在以某种方式引用operator ()时)。

因此,生成错误消息的通用lambda应实例化,因为使用了static_if的第二个重载,在参数f <的定义中em> not 被叫。由于未使用通用lambda operator (),编译器应该简单地忽略它。

我的推理在哪里错了?

3 个答案:

答案 0 :(得分:4)

在对已接受答案的评论中,您会问是否有更好的解决方案。可以说下面是一个(实时代码here)。你不是在本地捕获x,而是将static_if概括为一般地将任何参数转发给两个lambdas中的任何一个,并使你的x成为这样的参数。那么你不需要在评论中使用虚拟变量跳舞。

template <class F, class G, class ... Args>
auto static_if (std::true_type, F && f, G && g, Args && ... args) {
    return std::forward<F> (f) (std::forward<Args>(args)...);
}   

template <class F, class G, class ... Args>
auto static_if (std::false_type, F && f, G && g, Args && ... args) {
    return std::forward<G> (g) (std::forward<Args>(args)...);
}


template <class T> std::true_type constexpr is_pointer (T *) { return {}; }
template <class T> std::false_type constexpr is_pointer (T) { return {}; }

int main() {
    int x = 5;
    static_if (
        is_pointer (x),
        [] (auto x) { std::cout << *x << std::endl; },
        [] (auto x) { std::cout << "no pointer" << std::endl; },
        x
    );
    return 0;
}

答案 1 :(得分:3)

x是非模板依赖类型,因此检查在模板检查的第一次传递中完成,因为即使对于未实例化的模板,static_assert(false, "")也会触发。

答案 2 :(得分:1)

[&x](auto){ std::cout<<*x<<'\n'; }是一个带有模板operator()的lambda。没有传递给它们的可能类型的模板operator()在C ++中是不合法的。

*x的有效性不会因模板参数而改变。

解决此问题的一个好办法是改变static_if的工作方式。

template <class F, class G>
auto static_branch (std::true_type, F && f, G && g) {
  return std::forward<F>(f);
}
template <class F, class G>
auto static_branch (std::false_type, F && f, G && g) {
  return std::forward<G>(g);
}
template <bool test, class F, class G>
auto static_branch(F && f, G && g) {
  return static_branch( std::integral_constant<bool, test>{}, std::forward<F>(f), std::forward<G>(g) );
}   

请注意,我们不会调用fg。相反,我们会返回一个或另一个。

int main() {
  int x = 5;
  auto branch = static_branch (
    is_pointer (x),
    [&] (auto&&x) { std::cout << *x << std::endl; },
    [&] (auto&&) { std::cout << "no pointer" << std::endl; }
  );
  branch(x);
  return 0;
}

现在x 传递到相应的lambda。这意味着只有当x是指针时才会评估*x代码。

  int* px = &x;
  static_branch (
    is_pointer(y),
    [&] (auto&&x) { std::cout << *x << std::endl; },
    [&] (auto&&) { std::cout << "no pointer" << std::endl; }
  )(y);
然后

将调用第一个分支。

您还可以等待C ++ 17,其中constexpr if可以解决您的问题;我不确定。