我有以下示例代码片段:
我们的想法是有一个容器类,这里称为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
在第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中工作的解决方案。)
答案 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); });