diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index ddb1e996e3096d4935580d0bd0b0d7ae0039dd9d..bcb9d21a6fc549bbc612aae6cceef52cd501bda4 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,9 @@
+2004-11-22  Giovanni Bajo  <giovannibajo@gcc.gnu.org>
+
+	PR c++/18354
+	* typeck.c (build_unary_op) <CONVERT_EXPR, NEGATE_EXPR>: Unify code.
+	Make sure the result is always a rvalue.
+
 2004-11-16  Giovanni Bajo  <giovannibajo@gcc.gnu.org>
 
 	* decl.c (start_preparsed_function): Call check_function_type even
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index b1a85d5a1f26ef20b82c1e41b0e3f257e2f25747..4caf051075c6776cc863aaf6851a18a7c0a4c8aa 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -3741,26 +3741,30 @@ build_unary_op (enum tree_code code, tree xarg, int noconvert)
 
   switch (code)
     {
+    /* CONVERT_EXPR stands for unary plus in this context.  */
     case CONVERT_EXPR:
-      /* This is used for unary plus, because a CONVERT_EXPR
-	 is enough to prevent anybody from looking inside for
-	 associativity, but won't generate any code.  */
-      if (!(arg = build_expr_type_conversion
-	    (WANT_ARITH | WANT_ENUM | WANT_POINTER, arg, true)))
-	errstring = "wrong type argument to unary plus";
-      else
-	{
-	  if (!noconvert)
-	    arg = default_conversion (arg);
-	  arg = build1 (NON_LVALUE_EXPR, TREE_TYPE (arg), arg);
-	}
-      break;
-
     case NEGATE_EXPR:
-      if (!(arg = build_expr_type_conversion (WANT_ARITH | WANT_ENUM, arg, true)))
-	errstring = "wrong type argument to unary minus";
-      else if (!noconvert && CP_INTEGRAL_TYPE_P (TREE_TYPE (arg)))
-	arg = perform_integral_promotions (arg);
+      {
+	int flags = WANT_ARITH | WANT_ENUM;
+	/* Unary plus (but not unary minus) is allowed on pointers.  */
+	if (code == CONVERT_EXPR)
+	  flags |= WANT_POINTER;
+	arg = build_expr_type_conversion (flags, arg, true);
+	if (!arg)
+	  errstring = (code == NEGATE_EXPR
+		       ? "wrong type argument to unary minus"
+		       : "wrong type argument to unary plus");
+	else
+	  {
+	    if (!noconvert && CP_INTEGRAL_TYPE_P (TREE_TYPE (arg)))
+	      arg = perform_integral_promotions (arg);
+
+	    /* Make sure the result is not a lvalue: a unary plus or minus
+	       expression is always a rvalue.  */
+	    if (real_lvalue_p (arg))
+	      arg = build1 (NON_LVALUE_EXPR, TREE_TYPE (arg), arg);
+	  }
+      }
       break;
 
     case BIT_NOT_EXPR:
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index b9ec394932c6ca0319df0ebdbc42d2290ceaa30b..3601740963151c0a8f815176894ac02255425578 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2004-11-22  Giovanni Bajo  <giovannibajo@gcc.gnu.org>
+
+	PR c++/18354
+	* g++.dg/template/nontype11.C: New test.
+
 2004-11-21  Roger Sayle  <roger@eyesopen.com>
 
 	PR middle-end/18520
diff --git a/gcc/testsuite/g++.dg/template/nontype11.C b/gcc/testsuite/g++.dg/template/nontype11.C
new file mode 100644
index 0000000000000000000000000000000000000000..d52eb9a3879b854242b6fe8a1f396bb8a6c3139c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/nontype11.C
@@ -0,0 +1,22 @@
+// { dg-do compile }
+// Origin: <fsm at robots dot ox dot ac dot uk>
+// PR c++/18354: Unary plus should not be wrapped in NON_LVALUE_EXPR
+
+template <int N>
+struct X { };
+
+const int n = 1;
+
+void f()
+{
+  X< 1> a;
+  X<-1> b;
+  X<+1> c;
+}
+
+void g()
+{
+  X< n> a;
+  X<-n> b;
+  X<+n> c;
+}