C ++:将模板函数作为参数传递给其他模板函数时的模板参数推导

时间:2017-02-28 16:35:18

标签: c++ templates c++14 first-class-functions

我有以下示例代码片段:

我们的想法是有一个容器类,这里称为import java.awt.*; import java.util.List; import java.util.ArrayList; import javax.swing.*; import javax.swing.event.*; import javax.swing.border.*; import javax.swing.table.*; public class TableComboBoxByRow extends JPanel { List<String[]> editorData = new ArrayList<String[]>(3); public TableComboBoxByRow() { setLayout( new BorderLayout() ); // Create the editorData to be used for each row editorData.add( new String[]{ "Red", "Blue", "Green" } ); editorData.add( new String[]{ "Circle", "Square", "Triangle" } ); editorData.add( new String[]{ "Apple", "Orange", "Banana" } ); // Create the table with default data Object[][] data = { {"Color", "Red"}, {"Shape", "Square"}, {"Fruit", "Banana"}, {"Plain", "Text"} }; String[] columnNames = {"Type","Value"}; DefaultTableModel model = new DefaultTableModel(data, columnNames); JTable table = new JTable(model) { // Determine editor to be used by row public TableCellEditor getCellEditor(int row, int column) { int modelColumn = convertColumnIndexToModel( column ); if (modelColumn == 1 && row < 3) { JComboBox<String> comboBox1 = new JComboBox<String>( editorData.get(row)); return new DefaultCellEditor( comboBox1 ); } else return super.getCellEditor(row, column); } }; JScrollPane scrollPane = new JScrollPane( table ); add( scrollPane ); // table.getColumnModel().getColumn(1).setCellRenderer(new ComboBoxRenderer2() ); } /* class ComboBoxRenderer2 extends DefaultTableCellRenderer { @Override public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); label.setIcon(UIManager.getIcon("Table.descendingSortIcon")); return label; } } */ private static void createAndShowUI() { JFrame frame = new JFrame("Table Combo Box by Row"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add( new TableComboBoxByRow() ); frame.setSize(200, 200); frame.setLocationByPlatform( true ); frame.setVisible( true ); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { createAndShowUI(); } }); } } ,我们可能希望通过mapping函数创建此容器的新版本,而不是当前内容。

Box

Try it out here

在第42行,创建了box3,模板参数推断/替换失败:

#include <iostream>
#include <tuple>

template <typename TContent>
class Box
{
  TContent d_content;

  public:

  Box(TContent content)
  :
    d_content(content)
  {}

  TContent content() const
  {
    return d_content;
  }

  template <typename Function>
  auto transform(Function fun) -> decltype(Box{fun(d_content)})
  {
    return Box{fun(d_content)};
  };

};

template <typename TElem>
std::tuple<TElem, TElem> toTuple(TElem thing)
{
  std::cout << "Transforming " << thing << "to tuple.\n";
  return std::make_tuple(thing, thing);
}

int main() {
  std::cout << "Hello World!\n";

  Box<int> mybox{10};
  Box<int> box2 = mybox.transform([](int content){ return content * 2;});
  std::cout << "Transformed box: " << box2.content() << '\n';
  Box<std::tuple<int, int>> box3 = mybox.transform(&toTuple); // <- Template argument deduction/substitution fails here!
  std::cout << "Transformed box: " << std::get<0>(box3.content()) << '\n';
}

当尝试将模板函数(函数模板?)传递给本身需要模板参数参数的函数时,似乎就是这种情况。

到目前为止,我发现避免这种情况的唯一方法是将所有模板函数包装在lambdas或其他非模板函数中。这当然不是最理想的,因为它引入了很多样板。

为什么在这种情况下模板参数推断失败,并且有办法改变main.cpp: In function 'int main()': main.cpp:42:60: error: no matching function for call to 'Box<int>::transform(<unresolved overloaded function type>)' Box<std::tuple<int, int>> box3 = mybox.transform(&toTuple); ^ main.cpp:22:8: note: candidate: template<class Function> decltype (Box<TContent>{fun(((Box<TContent>*)this)->Box<TContent>::d_content)}) Box<TContent>::transform(Function) [with Function = Function; TContent = int] auto transform(Function fun) -> decltype(Box{fun(d_content)}) ^~~~~~~~~ main.cpp:22:8: note: template argument deduction/substitution failed: main.cpp:42:60: note: couldn't deduce template parameter 'Function' Box<std::tuple<int, int>> box3 = mybox.transform(&toTuple); ^ exit status 1 类(和/或其Box成员函数)的代码以确保模板参数演绎 工作吗?

(给定的代码是C ++ 11,因为repl.it还不支持c ++ 14.C ++ 14的主要区别在于transform的尾随返回类型可以但是,错误仍然是一样的。我很满意(仅)在C ++ 14中工作的解决方案。)

1 个答案:

答案 0 :(得分:2)

在你的例子中:

template <typename Function>
auto transform(Function fun) -> decltype(Box{fun(d_content)})

Box是一个模板,而不是一个类。在课堂内,Box注入类名,它始终专指Box<TContent>。因此,如果您需要更改类型(因为transform可能需要这样做),这将无法正常工作。您需要根据Box指定哪个 Function

template <class Function,
  class U = std::result_of_t<Function&(TContent)>>
Box<U> transform(Function fun)
{
  return Box<U>{fun(d_content)}; // or even better, Box<U>{std::invoke(fun, d_content)}
};

第二个问题是你打电话的时候:

Box<std::tuple<int, int>> box3 = mybox.transform(&toTuple);

toTuple是一个函数模板,而不是函数。您无法将其传递给另一个函数模板,因为它没有类型,因此无法推断。和以前一样,您需要指定您想要的 toTuple

Box<std::tuple<int, int>> box3 = mybox.transform(toTuple<int>);

或者将整个事物包装在lambda中(这是一个简化的实现,它不关心副本,引用或SFINAE):

Box<std::tuple<int, int>> box3 = mybox.transform([](auto x) { return toTuple(x); });