Template argument deduction fails when using braced initializer list

妖精的绣舞 提交于 2021-01-27 19:03:17

问题


I'm trying to use template argument deduction in the 'perpendicular()' function:

#include <iostream>

template <typename component = double>
struct offset {
  component x;
  component y;
};

template <typename component>
offset(component x, component y) -> offset<component>;

template <typename component>
offset<component> perpendicular(offset<component> const &o) {
  return offset{o.y, -o.x};
}

template <typename component>
std::ostream &operator<<(std::ostream &s, offset<component> const &o) {
  return s << '(' << o.x << ", " << o.y << ')';
}

int main() {
  std::cout << perpendicular({3.1, 1.2}) << '\n';
  return 0;
}

This however doesn't compile; Clang (with -std='c++17') says: candidate template ignored: couldn't infer template argument 'component' offset<component> perpendicular(offset<component> const &o) {.

Should I resign to writing perpendicular(offset{1.0, 2.0}) or is there a way to give the compiler a hint?


回答1:


Issue with {/*..*/} is that it has no type, and can mostly only be deduced as std::initializer_list<T> or T[N].

So following would allow desired syntax:

template <typename component>
offset<component> perpendicular(component const (&o)[2]) {
    return offset{o[1], -o[0]};
    // return perpendicular(offset{o[0], o[1]});
}

Demo




回答2:


Jarod42's answer gives you the syntax you want, but I subjectively think it's not ideal. You originally wanted to pass in an offset, but now you are passing an array and turning it into an offset. It's a weird relationship of types.

Instead of a struct and separate functions, just put it all into an Offset class. It's not really any extra work, and makes for better C++. What you have is more akin to object-oriented C.

#include <iostream>

// Create a self-contained class
template <typename Component = double>
class Offset {
 public:
  Offset(Component x, Component y) : x(x), y(y) {}

  // No longer requires function parameters
  Offset const perpendicular() const { return Offset(y, -x); }

  // I appreciate your use of east const
  friend std::ostream& operator<<(std::ostream& sout,
                                  Offset<Component> const& o) {
    return sout << '(' << o.x << ", " << o.y << ')';
  }

 private:
  Component x;
  Component y;
};

int main() {
  // Subjectively much cleaner to read and understand
  std::cout << Offset{3.1, 1.2}.perpendicular() << '\n';
  return 0;
}

For future reference, can use decltype(auto) as your return type and forego the trailing return type syntax altogether as of C++14.




回答3:


One option is to add an overload to perpendicular that takes two values.

template <typename component>
offset<component> perpendicular(component v1, component v2) {
    return {v2, -v1};
}

This could also be made more generic with parameter packs, possibly combined with std::common_type.



来源:https://stackoverflow.com/questions/58119140/template-argument-deduction-fails-when-using-braced-initializer-list

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!