lambda的以下用法是错误的,脆弱的还是愚蠢的?它适用于VC ++ 2012,但我担心有一些变量参数/ lambda堆栈交互会使这种情况变得危险。
class
ArgumentException : public std::runtime_error
{
public:
ArgumentException(
const char* format_,
... )
: std::runtime_error(
[&]()
{
char buffer[2048];
va_list arguments;
va_start ( arguments, format_ );
int writtenCount = vsnprintf( buffer, 2048, format_, arguments );
va_end ( arguments );
return std::string(buffer);
}() )
{
}
};
答案 0 :(得分:4)
C ++ 11标准通过va_list
(第18.10节,表37)显式地使<cstdarg>
和支持宏可用,但不试图限制它们的使用或(重新)定义它们的含义,所以我要转向C标准。
§7.15.1.4说:
void va_start(va_list ap,
parmN);
参数 parmN 是函数定义中变量参数列表中最右边参数的标识符(
, ...
之前的参数)。如果使用register
存储类声明参数 parmN ,使用函数或数组类型,或者使用与结果类型不兼容的类型在应用默认参数促销后,行为未定义。
在你的情况下,format_
不是一个参数(它是你的lambda中捕获的变量),调用va_start
的函数甚至不是带有变量参数列表的函数,所以你可以说是在未定义的行为领域。更不用说C语言的参数提升规则不能处理引用类型,因此它无法正确处理format_
是引用的事实,而不是直接指针。
据我所知,在构造函数的初始化列表中使用variadic参数在语法上是不可行的。(See this guy below who did it.)但是,您可以使用可变参数模板将参数转发给“干净”C风格的可变参数功能:
#include <cstdarg>
#include <cstdio>
#include <string>
std::string stringprintf(const char* format, ...)
{
char buffer[0x2000];
va_list ap;
va_start(ap, format);
vsnprintf(buffer, sizeof buffer, format, ap);
va_end(ap);
return buffer;
}
class ArgumentException : public std::runtime_error
{
public:
template<typename... T>
ArgumentException(const char* format, T... arguments)
: std::runtime_error(stringprintf(format, arguments...))
{ }
};
另请考虑使用<stdexcept>
的{{1}}异常子类。
答案 1 :(得分:2)
我不建议这样做,所以这更像是一种练习,无论是否可以在没有可变参数模板的情况下编写符合要求的实现。我认为这是,虽然它不优雅或漂亮:
#include <stdexcept>
#include <cstdarg>
std::string
helper(const char *fmt, va_list args) {
char buffer[2048];
int writtenCount = vsnprintf(buffer, sizeof buffer, fmt, args);
return std::string(buffer);
}
class ArgumentException : public std::runtime_error {
public:
ArgumentException(const char* fmt, ...)
: std::runtime_error(helper(fmt, (va_start(args, fmt), args))) {
va_end(args);
}
private:
va_list args;
};
顺便说一下,原来的尝试给了我一个g ++错误,声称我在一个带有固定参数的函数中使用了va_start,这是正确的,我相信。
答案 2 :(得分:0)
为了后人,在这里添加WhozCraig的解决方案:
class ArgumentException : public std::runtime_error
{
private:
static std::string mkmsg(const char *fmt, ...)
{
char buffer[2048];
va_list args;
va_start ( args, fmt );
int writtenCount = vsnprintf( buffer, 2048, fmt, args );
va_end ( args );
if (writtenCount<=0)
*buffer = 0;
return buffer;
}
public:
template<class... Args>
ArgumentException(const char* fmt, Args&&... args )
: std::runtime_error( mkmsg(fmt, std::forward<Args>(args)... ) )
{
}
};