如何在C ++中使用lambda作为模板参数和默认值?

时间:2017-02-13 14:49:51

标签: c++ lambda c++17

我想做一件简单的事情:

template <typename P>
void DoUntil(P predicate = [] { return false; });

显然这不起作用 - 我必须使用模板参数:

int main() { DoUntil(); }

但是这句话也不起作用--Clang给出了一个错误:

  

错误:没有匹配函数来调用...
  注意:候选模板被忽略:无法推断模板参数&#39; P&#39;

如果我在没有参数的情况下调用该函数,那么编译器无法从默认参数中推断出类型:

std::function<>

我不想以任何方式使用D:\trydjango\src>python manage.py makemigrations No changes detected D:\trydjango\src>python manage.py migrate Operations to perform: Apply all migrations: contenttypes, auth, sessions, admin, posts Running migrations: Rendering model states... DONE Applying posts.0009_auto_20170213_1754...Traceback (most recent call last): File "manage.py", line 10, in <module> execute_from_command_line(sys.argv) File "C:\python35\lib\site-packages\django\core\management\__init__.py", line 350, in execute_from_command_line utility.execute() File "C:\python35\lib\site-packages\django\core\management\__init__.py", line 342, in execute self.fetch_command(subcommand).run_from_argv(self.argv) File "C:\python35\lib\site-packages\django\core\management\base.py", line 348, in run_from_argv self.execute(*args, **cmd_options) File "C:\python35\lib\site-packages\django\core\management\base.py", line 399, in execute output = self.handle(*args, **options) File "C:\python35\lib\site-packages\django\core\management\commands\migrate.py ", line 200, in handle executor.migrate(targets, plan, fake=fake, fake_initial=fake_initial) File "C:\python35\lib\site-packages\django\db\migrations\executor.py", line 92 , in migrate self._migrate_all_forwards(plan, full_plan, fake=fake, fake_initial=fake_ini tial) File "C:\python35\lib\site-packages\django\db\migrations\executor.py", line 12 1, in _migrate_all_forwards state = self.apply_migration(state, migration, fake=fake, fake_initial=fake_ initial) File "C:\python35\lib\site-packages\django\db\migrations\executor.py", line 19 8, in apply_migration state = migration.apply(state, schema_editor) File "C:\python35\lib\site-packages\django\db\migrations\migration.py", line 1 23, in apply operation.database_forwards(self.app_label, schema_editor, old_state, projec t_state) File "C:\python35\lib\site-packages\django\db\migrations\operations\fields.py" , line 62, in database_forwards field, File "C:\python35\lib\site-packages\django\db\backends\sqlite3\schema.py", lin e 221, in add_field self._remake_table(model, create_fields=[field]) File "C:\python35\lib\site-packages\django\db\backends\sqlite3\schema.py", lin e 103, in _remake_table self.effective_default(field) File "C:\python35\lib\site-packages\django\db\backends\base\schema.py", line 2 10, in effective_default default = field.get_db_prep_save(default, self.connection) File "C:\python35\lib\site-packages\django\db\models\fields\__init__.py", line 728, in get_db_prep_save prepared=False) File "C:\python35\lib\site-packages\django\db\models\fields\__init__.py", line 1461, in get_db_prep_value value = self.get_prep_value(value) File "C:\python35\lib\site-packages\django\db\models\fields\__init__.py", line 1440, in get_prep_value value = super(DateTimeField, self).get_prep_value(value) File "C:\python35\lib\site-packages\django\db\models\fields\__init__.py", line 1296, in get_prep_value return self.to_python(value) File "C:\python35\lib\site-packages\django\db\models\fields\__init__.py", line 1399, in to_python parsed = parse_datetime(value) File "C:\python35\lib\site-packages\django\utils\dateparse.py", line 93, in pa rse_datetime match = datetime_re.match(value) TypeError: expected string or bytes-like object D:\trydjango\src>

我的问题还有其他可能的解决方案吗?

2 个答案:

答案 0 :(得分:8)

使用函数重载而不是默认参数功能。创建一个除模板函数之外不带参数的非模板函数:

void DoUntil() ;

template <typename P>
void DoUntil(P predicate) ;

无参数版本只需使用您想用作默认谓词的lambda调用模板版本:

void DoUntil() { DoUntil([] { return false; }); }

您的原始方法存在的问题是,您通过指定默认参数值尝试提供默认的模板专门化,但未指定默认的模板类型。即使没有涉及lambdas,以下内容也不会起作用,因为T没有默认类型,即使t有默认值:

template <typename T>
void Foo(T t = 3);

需要的是使用T<typename T = int>指定默认类型。

如WhiZTiM的回答所述,涉及lambda函数的案例的默认类型必须使用decltype推导出来。这当然是因为lambda具有只有编译器才知道的唯一类型。

答案 1 :(得分:4)

lambda是一个没有默认构造函数的匿名类型(原因,如果可用,你可以使用它的复制/移动构造函数)。如果你必须采用lambda方式,你可以这样做:

namespace detail{ auto predicate = [] { return false; }; }

template <typename P = decltype(detail::predicate)>
void DoUntil(P pred = detail::predicate);

而不是试图摆弄 lambdas 。你可以走老路:

namespace detail{
    struct DefaultPredicate{ bool operator()() const { return false; } };
}

template <typename P = detail::DefaultPredicate>
void DoUntil(P predicate = P{});

或者更好的是Kyle Strand answered